claude-dev-env 1.41.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +8 -0
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +232 -8
- package/_shared/pr-loop/scripts/code_rules_gate.py +293 -8
- package/_shared/pr-loop/scripts/fix_hookspath.py +96 -5
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +124 -20
- package/_shared/pr-loop/scripts/post_audit_thread.py +4 -4
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +90 -0
- package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_settings_keys_constants.py +2 -0
- package/_shared/pr-loop/scripts/preflight.py +13 -31
- package/_shared/pr-loop/scripts/reviews_disabled.py +2 -16
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +76 -33
- package/_shared/pr-loop/scripts/tests/conftest.py +1 -51
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +385 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +37 -2
- package/_shared/pr-loop/scripts/tests/test_claude_settings_keys_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +2 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_preflight.py +17 -52
- package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +5 -3
- package/agents/pr-description-writer.md +50 -140
- package/docs/PR_DESCRIPTION_GUIDE.md +101 -102
- package/hooks/_gh_pr_author_swap_utils.py +1 -1
- package/hooks/blocking/bot_mention_comment_blocker.py +4 -10
- package/hooks/blocking/code_rules_enforcer.py +217 -99
- package/hooks/blocking/code_rules_path_utils.py +8 -1
- package/hooks/blocking/destructive_command_blocker.py +1 -1
- package/hooks/blocking/es_exe_path_rewriter.py +7 -13
- package/hooks/blocking/gh_body_arg_blocker.py +6 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +5 -5
- package/hooks/blocking/gh_pr_author_restore.py +5 -5
- package/hooks/blocking/hedging_language_blocker.py +4 -10
- package/hooks/blocking/md_path_exemptions.py +205 -0
- package/hooks/blocking/md_to_html_blocker.py +48 -20
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -11
- package/hooks/blocking/pr_description_enforcer.py +626 -41
- package/hooks/blocking/question_to_user_enforcer.py +4 -10
- package/hooks/blocking/state_description_blocker.py +6 -12
- package/hooks/blocking/tdd_enforcer.py +1 -1
- package/hooks/blocking/test_bot_mention_comment_blocker.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer.py +3 -3
- package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -2
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +184 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +82 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +29 -29
- package/hooks/blocking/test_gh_body_arg_blocker.py +7 -8
- package/hooks/blocking/test_gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +1 -1
- package/hooks/blocking/test_hedging_language_blocker.py +2 -2
- package/hooks/blocking/test_md_to_html_blocker.py +463 -8
- package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +1 -1
- package/hooks/blocking/test_pr_description_enforcer.py +1210 -13
- package/hooks/blocking/test_question_to_user_enforcer.py +1 -1
- package/hooks/blocking/windows_rmtree_blocker.py +5 -11
- package/hooks/diagnostic/hook_log_extractor.py +1 -1
- package/hooks/diagnostic/hook_log_init.py +1 -1
- package/hooks/diagnostic/hook_log_stop_wrapper.py +1 -1
- package/hooks/diagnostic/test_hook_log_extractor.py +1 -1
- package/hooks/diagnostic/test_hook_log_init.py +2 -2
- package/hooks/diagnostic/test_hook_log_stop_wrapper.py +1 -1
- package/hooks/git-hooks/gate_utils.py +1 -1
- package/hooks/git-hooks/pre_commit.py +1 -1
- package/hooks/git-hooks/pre_push.py +1 -1
- package/hooks/git-hooks/test_config.py +5 -5
- package/hooks/git-hooks/test_pre_push.py +6 -6
- package/hooks/{config → hooks_constants}/code_rules_enforcer_constants.py +37 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +28 -0
- package/hooks/hooks_constants/md_to_html_blocker_constants.py +82 -0
- package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_state.py +1 -1
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +154 -0
- package/hooks/{config → hooks_constants}/pre_tool_use_stdin.py +1 -1
- package/hooks/{config → hooks_constants}/project_paths_reader.py +2 -2
- package/hooks/{config → hooks_constants}/test_banned_identifiers_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_dynamic_stderr_handler.py +1 -1
- package/hooks/{config → hooks_constants}/test_hardcoded_user_path_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_hook_log_extractor_constants.py +2 -2
- package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +110 -0
- package/hooks/{config → hooks_constants}/test_messages.py +2 -6
- package/hooks/{config → hooks_constants}/test_path_rewriter_constants.py +1 -1
- package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +292 -0
- package/hooks/{config → hooks_constants}/test_pre_tool_use_stdin.py +2 -2
- package/hooks/{config → hooks_constants}/test_project_paths_reader.py +3 -3
- package/hooks/{config → hooks_constants}/test_session_env_cleanup_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_setup_project_paths_constants.py +2 -2
- package/hooks/{config → hooks_constants}/test_unused_module_import_constants.py +1 -1
- package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +5 -11
- package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +1 -1
- package/hooks/session/gh_pr_author_session_cleanup.py +5 -6
- package/hooks/session/session_env_cleanup.py +4 -10
- package/hooks/session/test_gh_pr_author_session_cleanup.py +1 -1
- package/hooks/session/test_untracked_repo_detector.py +2 -2
- package/hooks/session/untracked_repo_detector.py +6 -12
- package/hooks/test__gh_pr_author_swap_utils.py +1 -1
- package/hooks/validators/run_all_validators.py +16 -5
- package/hooks/validators/test_output_formatter.py +46 -0
- package/hooks/workflow/doc_gist_auto_publish.py +1 -1
- package/hooks/workflow/md_to_html_companion.py +8 -15
- package/hooks/workflow/test_md_to_html_companion.py +184 -23
- package/package.json +1 -1
- package/rules/ask-user-question-required.md +1 -1
- package/rules/vault-context.md +1 -1
- package/scripts/{config → dev_env_scripts_constants}/timing.py +1 -1
- package/scripts/setup_project_paths.py +49 -11
- package/scripts/sweep_empty_dirs.py +10 -1
- package/scripts/test_setup_project_paths.py +2 -2
- package/scripts/test_sweep_empty_dirs.py +2 -6
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/init_loop_state.py +1 -1
- package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +1 -1
- package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +2 -2
- package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +2 -2
- package/skills/bugteam/PROMPTS.md +1 -1
- package/skills/bugteam/SKILL.md +1 -1
- package/skills/bugteam/reference/github-pr-reviews.md +1 -1
- package/skills/bugteam/scripts/{_claude_permissions_common.py → _bugteam_permissions_common.py} +110 -13
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +1 -13
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +1 -16
- package/skills/bugteam/scripts/bugteam_preflight.py +1 -13
- package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +69 -0
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +117 -12
- package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +1 -1
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +71 -25
- package/skills/bugteam/scripts/{test__claude_permissions_common.py → test__bugteam_permissions_common.py} +4 -4
- package/skills/bugteam/scripts/test_agent_config_carveout.py +356 -0
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -26
- package/skills/bugteam/scripts/{test_claude_permissions_common.py → test_bugteam_permissions_common.py} +3 -66
- package/skills/bugteam/scripts/test_bugteam_preflight.py +2 -27
- package/skills/bugteam/scripts/windows_safe_rmtree.py +1 -1
- package/skills/doc-gist/SKILL.md +1 -1
- package/skills/doc-gist/scripts/gist_upload.py +1 -1
- package/skills/implement/SKILL.md +66 -0
- package/skills/implement/scripts/append_note.py +133 -0
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +12 -0
- package/skills/implement/scripts/test_append_note.py +191 -0
- package/skills/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
- package/skills/pr-converge/{config → pr_converge_skill_constants}/constants.py +6 -1
- package/skills/pr-converge/scripts/check_bugbot_ci.py +2 -2
- package/skills/pr-converge/scripts/check_convergence.py +175 -29
- package/skills/pr-converge/scripts/check_pending_reviews.py +2 -2
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +2 -2
- package/skills/pr-converge/scripts/post_fix_reply.py +2 -2
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
- package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/pr_converge_constants.py +1 -1
- package/skills/pr-converge/scripts/reflow_skill_md.py +90 -16
- package/skills/pr-converge/scripts/test_check_bugbot_ci.py +1 -1
- package/skills/pr-converge/scripts/test_check_convergence.py +324 -0
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +0 -31
- package/skills/refine/SKILL.md +257 -0
- package/skills/refine/templates/implementation-notes-template.html +56 -0
- package/skills/refine/templates/plan-template.md +60 -0
- package/skills/session-log/SKILL.md +98 -233
- package/_shared/pr-loop/scripts/config/claude_permissions_constants.py +0 -36
- package/hooks/config/pr_description_enforcer_constants.py +0 -19
- package/hooks/config/test_pr_description_enforcer_constants.py +0 -82
- package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +0 -20
- package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +0 -55
- package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +0 -55
- package/skills/pr-converge/scripts/evict_cached_config_modules.py +0 -20
- package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +0 -22
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/__init__.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/code_rules_gate_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/fix_hookspath_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/post_audit_thread_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/preflight_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/reviews_disabled_constants.py +0 -0
- /package/hooks/git-hooks/{config.py → git_hooks_constants/__init__.py} +0 -0
- /package/hooks/{config → hooks_constants}/__init__.py +0 -0
- /package/hooks/{config → hooks_constants}/any_type_config.py +0 -0
- /package/hooks/{config → hooks_constants}/banned_identifiers_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/blocking_check_limits.py +0 -0
- /package/hooks/{config → hooks_constants}/bot_mention_comment_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/convergence_branch_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/doc_gist_auto_publish_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/dynamic_stderr_handler.py +0 -0
- /package/hooks/{config → hooks_constants}/gh_pr_author_swap_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hardcoded_user_path_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hook_log_extractor_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/html_companion_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/inline_tuple_string_magic_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/messages.py +0 -0
- /package/hooks/{config → hooks_constants}/path_rewriter_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/session_env_cleanup_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/setup_project_paths_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/state_description_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_check_config.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_import_binding_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/sys_path_insert_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/unused_module_import_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/windows_rmtree_blocker_constants.py +0 -0
- /package/{skills/_shared/pr-loop/scripts/config → hooks/lifecycle}/__init__.py +0 -0
- /package/{skills/bugteam/scripts/config → hooks/session}/__init__.py +0 -0
- /package/scripts/{config → dev_env_scripts_constants}/__init__.py +0 -0
- /package/skills/{doc-gist/scripts/config → _shared/pr-loop/scripts/skills_pr_loop_constants}/__init__.py +0 -0
- /package/skills/_shared/pr-loop/scripts/{config → skills_pr_loop_constants}/path_resolver_constants.py +0 -0
- /package/skills/{pr-converge/config → bugteam/scripts/bugteam_scripts_constants}/__init__.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_code_rules_gate_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_fix_hookspath_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_preflight_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/probe_code_rules_enforcer_check_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/reflow_skill_md_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/windows_safe_rmtree_constants.py +0 -0
- /package/skills/{pr-converge/scripts/config → doc-gist/scripts/doc_gist_scripts_constants}/__init__.py +0 -0
- /package/skills/doc-gist/scripts/{config → doc_gist_scripts_constants}/gist_upload_constants.py +0 -0
- /package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/reflow_skill_md_constants.py +0 -0
|
@@ -1,84 +1,76 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-log
|
|
3
3
|
description: >-
|
|
4
|
-
Log a session report
|
|
4
|
+
Log a session report by handing the HTML authorship to doc-gist (which designs fresh per session and auto-publishes via the `<!-- @publish-as-gist -->` marker hook), then track vault context, extract unrecorded decisions, tidy the project's session folder, and output a /rename command. Use when the user says /session-log, journal this session, log this work, session report, or any variation of "summarize/log/record this session". Also triggers on "save session", "capture session", or "document what we did".
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Session Log
|
|
8
8
|
|
|
9
9
|
## Overview
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The HTML artifact is doc-gist's job end-to-end. Session-log owns everything around it: where the file lives in the vault, what number it gets, the frontmatter metadata contract, post-write vault tracking, decision extraction, project-folder hygiene, and the closing `/rename` hand-off.
|
|
12
12
|
|
|
13
13
|
**Announce at start:** "I'm logging this session."
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Why this skill delegates HTML to doc-gist
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Sessions come in many shapes — convergence loops, feature builds, research dives, incidents, refactors, decisions. A single h2-emoji-list template forces every session into the same form regardless of fit, and the artifact reads as a process log rather than a substance log. Doc-gist's principle: *"design fresh per request, drawing on a gallery of 20 HTML shape patterns."* Each session report gets the shape that fits the session.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
- **Avoid historical and comparative language.** The `state_description_blocker` hook enforces this for markdown and code files. While the hook does not scan `.html`, the same present-tense, current-state style applies to session reports. The trigger pattern set lives in `~/.claude/rules/no-historical-clutter.md`.
|
|
21
|
-
- **doc-gist uses `--description` for gist title text.** `gist_upload.py` uploads HTML verbatim with no content parsing. Pass `--description "Session [N] — [Title]"` to set the gist description.
|
|
22
|
-
- **doc-gist preview URL takes a few seconds.** The htmlpreview.github.io renderer fetches the raw gist on first hit. Quote both URLs and tell the user to refresh once if the page is blank.
|
|
23
|
-
- **gh must be authenticated.** Running gist_upload.py with `gh` unauthenticated prints the auth prompt and exits non-zero. Surface that message to the user; the local HTML file in the vault is still the canonical artifact, so Step 6 still runs.
|
|
24
|
-
- **Obsidian frontmatter index is sacrificed.** Obsidian's native YAML-frontmatter parser reads only `.md` files. HTML files do not appear in the Obsidian UI's frontmatter index. Search by content still works; search by `type: session-report` does not.
|
|
25
|
-
- **Existing files require the Edit tool.** The `write_existing_file_blocker` hook rejects the Write tool on existing paths. Use Write only when creating a fresh session report; use Edit for Step 2's append and Step 4's auto-fixes.
|
|
19
|
+
The gallery lives at `~/.claude/skills/doc-gist/references/examples/`. Patterns that usually fit session reports:
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
| Session character | Gallery shape to study |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Feature build / PR ships | `17-pr-writeup.html` |
|
|
24
|
+
| Incident, debugging arc, convergence loop | `12-incident-report.html` |
|
|
25
|
+
| Status update / weekly progress | `11-status-report.html` |
|
|
26
|
+
| Implementation plan or decision record | `16-implementation-plan.html` |
|
|
27
|
+
| Exploration of multiple approaches | `01-exploration-code-approaches.html` |
|
|
28
|
+
| Code-explainer with module map | `04-code-understanding.html` |
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
The session designer reads the matching gallery file, then designs the report in that shape. **Adapt, do not copy.**
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
## Gotchas
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
- **Doc-gist's auto-publish hook fires on Write/Edit of any HTML containing `<!-- @publish-as-gist -->`.** Session-log composes the HTML with the marker so auto-publish runs by default — sessions are intended for sharing with collaborators. The hook prints the gist + preview URLs to tool output; capture both.
|
|
35
|
+
- **`gh` must be authenticated.** Auto-publish runs `gh gist create`. If `gh` is unauthenticated, `gh gist create` writes its error message; `gist_upload.py` surfaces it from stderr when present and falls back to stdout when stderr is empty (so the error always appears somewhere in the tool result). The hook exits 0 (does not block the Write). Look at the full tool result, then surface the error to the user; the local vault HTML is still the canonical artifact, so the remaining steps still run.
|
|
36
|
+
- **Vault paths sit outside `.claude/`.** Headless vault paths (e.g., `$OBSIDIAN_VAULT_PATH`) resolve outside the project tree. The `md_to_html_blocker` PreToolUse hook blocks `.md` writes unless the path is exempt — exemptions include any path containing `/.claude/` and README/CHANGELOG files at repo root. Session reports use HTML, which the hook ignores entirely.
|
|
37
|
+
- **Sessions describe current state by convention.** The state_description_blocker hook does not scan .html, but the rule at `~/.claude/rules/no-historical-clutter.md` applies as a writing standard — skip historical and comparative language when composing the report; the rule file lists the full trigger set.
|
|
38
|
+
- **`write_existing_file_blocker` rejects Write on existing paths.** Use Write only when creating a fresh session report; use Edit for the vault-context append in step 3.
|
|
39
|
+
- **Each Write/Edit of the marked HTML creates a fresh gist with a new ID.** `gist_upload.py` calls `gh gist create` with no lookup of any prior gist. Step 3 performs two Edits and Step 5's tidy may Edit the current session a third time — each Edit produces a different gist URL than its predecessor. Quote the URLs from the FINAL publish (the most recent auto-publish run for the session being created — the second Step-3 Edit when Step 5 does not touch the current session, or the Step 5 Edit when it does) to the user; never embed a URL inside the HTML that a later Edit then re-publishes.
|
|
40
|
+
- **Obsidian frontmatter index is HTML-blind.** Obsidian's native YAML-frontmatter parser reads only `.md` files. HTML files do not appear in Obsidian's frontmatter index. Search by content still works; search by `type: session-report` does not.
|
|
41
|
+
- **Auto-published gists carry the default `gist_upload.py` description (`HTML artifact`), not a session-specific title.** The auto-publish hook invokes `gist_upload.py` with only `--input` and `--no-open` — it has no session-title context to pass as `--description`. Browsing or sharing gists, operators see "HTML artifact" rather than `Session [N] — [Title] — session-log — [Project]`. Workaround: after the canonical Edit completes, edit the existing gist's description in place with `gh gist edit <gist-id> --desc "Session [N] — [Title] — session-log — [Project]"` (this updates the existing gist's metadata; no new gist ID is created) if a meaningful title matters for that share. The HTML frontmatter inside the file remains the authoritative session metadata regardless of the gist's external description.
|
|
42
|
+
|
|
43
|
+
## Backend Detection (run before Step 1)
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
Determine which storage backend is available. First success wins.
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| Write session reports | Write tool to `.html` path | Write tool to `.html` path |
|
|
40
|
-
| Search prior sessions | Bash `ls` + Grep | Bash `ls` + Grep |
|
|
41
|
-
| Session number detection | parse filenames | parse filenames |
|
|
42
|
-
| Frontmatter | YAML inside HTML comment | YAML inside HTML comment |
|
|
43
|
-
| Sync | Obsidian Sync | none (local only) |
|
|
47
|
+
1. **Headless vault** — Bash `ob --version` to verify the obsidian-headless CLI is installed. Check `OBSIDIAN_VAULT_PATH` env var or `~/.claude/vault/` for a vault directory. When the CLI check succeeds AND at least one of those paths resolves to a vault directory, set `backend = "headless"`.
|
|
48
|
+
2. **Local vault** — fall back to `~/.claude/vault/`. Create `~/.claude/vault/sessions` via `mkdir -p` if missing. Set `backend = "local"`.
|
|
44
49
|
|
|
45
|
-
**Session
|
|
46
|
-
- List files in the project directory via Bash `ls`
|
|
47
|
-
- Parse filenames matching `[N]. *.html` or `[N]. *.md` to preserve sequence across the format migration
|
|
48
|
-
- Highest N + 1. If directory does not exist, create it and start at 1
|
|
50
|
+
**Session-number detection:** Bash `ls` the project's session folder, parse filenames matching `[N]. *.html` or `[N]. *.md` to preserve sequence across the format migration. Highest N + 1. New project → start at 1.
|
|
49
51
|
|
|
50
52
|
**Output paths:**
|
|
51
53
|
- headless: `$OBSIDIAN_VAULT_PATH/sessions/[Project]/[N]. [Title].html` (falls back to `~/.claude/vault/` when the env var is unset)
|
|
52
54
|
- local: `~/.claude/vault/sessions/[Project]/[N]. [Title].html`
|
|
53
55
|
|
|
54
|
-
Announce the backend: "Using headless vault at [path]." or "Using local vault at ~/.claude/vault/.
|
|
56
|
+
Announce the backend: "Using headless vault at [path]." or "Using local vault at ~/.claude/vault/. Install obsidian-headless and set the `OBSIDIAN_VAULT_PATH` environment variable (PowerShell: `$env:OBSIDIAN_VAULT_PATH = '<path>'`; POSIX shells: `export OBSIDIAN_VAULT_PATH=<path>`) to enable sync."
|
|
55
57
|
|
|
56
58
|
---
|
|
57
59
|
|
|
58
|
-
## Step 1:
|
|
60
|
+
## Step 1: Compose Session Metadata
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Review the conversation to identify the session's primary outcome and the small set of facts a cold reader needs.
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
- **Project name:** infer from conversation context
|
|
64
|
-
- **Session number:** list the project's vault folder via Bash `ls`, parse `[N]. *.html` and `[N]. *.md` filenames, take highest+1. If no prior sessions, start at 1.
|
|
65
|
-
- **Date:** today's date
|
|
66
|
-
- **Title:** a 2-5 word summary of the session's primary outcome or focus area. Pick the single most important thing that happened. Examples: "Amazon Auth Migration", "Source Loading Fix", "Vault Reorganization". Avoid generic titles like "Bug Fixes" or "Various Updates".
|
|
64
|
+
Resolve the metadata used by the frontmatter and the vault path:
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
- **Project name:** infer from conversation context
|
|
67
|
+
- **Session number:** from backend detection above
|
|
68
|
+
- **Date:** today's date
|
|
69
|
+
- **Title:** a 2–5 word summary of the session's primary outcome. Examples: "Amazon Auth Migration", "Source Loading Fix", "PR 475 Convergence". Avoid generic titles like "Bug Fixes".
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
The vault note is a self-contained HTML file. Frontmatter lives inside an HTML comment so it is human-readable but does not affect rendering. Styling is minimal so doc-gist's template wraps the body cleanly in Step 5.
|
|
71
|
+
The frontmatter contract every session report carries (inside an HTML comment, as the first child of `<body>`):
|
|
73
72
|
|
|
74
73
|
```html
|
|
75
|
-
<!DOCTYPE html>
|
|
76
|
-
<html lang="en">
|
|
77
|
-
<head>
|
|
78
|
-
<meta charset="utf-8">
|
|
79
|
-
<title>Session [N] — [Title]</title>
|
|
80
|
-
</head>
|
|
81
|
-
<body>
|
|
82
74
|
<!--
|
|
83
75
|
type: session-report
|
|
84
76
|
project: [name]
|
|
@@ -86,253 +78,126 @@ session: [N]
|
|
|
86
78
|
date: [YYYY-MM-DD]
|
|
87
79
|
status: completed|in-progress|blocked
|
|
88
80
|
blocked: true|false
|
|
89
|
-
|
|
81
|
+
vault_context_retrieved: true|false
|
|
82
|
+
tags: [session, [project-tag], [topic-tags]]
|
|
90
83
|
-->
|
|
91
|
-
|
|
92
|
-
<h1>Session [N] Report — [Month Day, Year]</h1>
|
|
93
|
-
|
|
94
|
-
<h2>[emoji] [Section Title]</h2>
|
|
95
|
-
<p>[1-3 sentence explanation of what happened and why it matters]</p>
|
|
96
|
-
<ul>
|
|
97
|
-
<li><strong>[Label]:</strong> [detail]</li>
|
|
98
|
-
</ul>
|
|
99
|
-
<p><strong>Fix:</strong> [what was done]</p>
|
|
100
|
-
|
|
101
|
-
<h2>[emoji] [Section with tabular data]</h2>
|
|
102
|
-
<p>[Context sentence]</p>
|
|
103
|
-
<table>
|
|
104
|
-
<thead><tr><th>#</th><th>Item</th><th>Status</th></tr></thead>
|
|
105
|
-
<tbody>
|
|
106
|
-
<tr><td>1</td><td>...</td><td>...</td></tr>
|
|
107
|
-
</tbody>
|
|
108
|
-
</table>
|
|
109
|
-
|
|
110
|
-
<h2>[emoji] Notes</h2>
|
|
111
|
-
<ul>
|
|
112
|
-
<li><strong>[Topic]:</strong> [detail]</li>
|
|
113
|
-
</ul>
|
|
114
|
-
|
|
115
|
-
</body>
|
|
116
|
-
</html>
|
|
117
84
|
```
|
|
118
85
|
|
|
119
|
-
|
|
86
|
+
Every session report carries this metadata block verbatim so vault search and the tidy step in step 5 work. **Initial values for Step 2's Write:** substitute concrete values for every placeholder — for `vault_context_retrieved`, write the literal value `false` (the safe default before Step 3's vault-MCP-tool scan completes). Step 3 then Edits this to `true` if any of the three vault MCP tools fired this session.
|
|
120
87
|
|
|
121
|
-
|
|
122
|
-
|-------|---------|----------|
|
|
123
|
-
| ✅ | Done/Fixed | A problem was resolved or a deliverable completed |
|
|
124
|
-
| 🚫 | Blocked | Something couldn't be done (external limit, dependency, etc.) |
|
|
125
|
-
| ⚠️ | Warning/Note | Important context, gotchas, or things to remember |
|
|
126
|
-
| 🔧 | In Progress | Work started but not finished |
|
|
127
|
-
| 📋 | Queued | Work identified but not yet started |
|
|
88
|
+
## Step 2: Compose the HTML via doc-gist's shape principles
|
|
128
89
|
|
|
129
|
-
|
|
90
|
+
Design the artifact for **this session's character**, drawing on the doc-gist gallery patterns listed above. The report must answer for a cold reader, from the H2 headers alone, three questions: *what shipped*, *why it matters*, *what impact it had*. Process narration (commit-by-commit walks, agent gotchas, retry counts) belongs at the end, not in the opening sections.
|
|
130
91
|
|
|
131
|
-
|
|
132
|
-
- **Explanatory paragraphs** (`<p>`) under each header -- not just bullets. Explain what happened and why.
|
|
133
|
-
- **Bold inline labels** for key facts: `<strong>Fix:</strong>`, `<strong>Account:</strong>`, etc.
|
|
134
|
-
- **Tables** (`<table>`) for anything with 3+ rows of structured data (queued items, test results, file lists)
|
|
135
|
-
- **Bullets** (`<ul><li>`) for lists of 2+ related items
|
|
136
|
-
- **Links** (`<a href="...">`) where useful: file paths, URLs, PR links
|
|
137
|
-
- **No inline `style=` attributes and no `<style>` block.** Doc-gist wraps the body in its own template; inner styles fight the wrapper.
|
|
138
|
-
|
|
139
|
-
### What NOT to include
|
|
140
|
-
|
|
141
|
-
- Play-by-play of debugging steps or failed approaches
|
|
142
|
-
- Process narration ("First I tried X, then Y")
|
|
143
|
-
- Redundant sections -- if nothing was blocked, skip the blocked section
|
|
144
|
-
- Historical or comparative language — see `~/.claude/rules/no-historical-clutter.md` for the trigger pattern set. The `state_description_blocker` hook rejects writes containing these patterns in markdown/code; the same rule applies to session report HTML.
|
|
145
|
-
|
|
146
|
-
### Example
|
|
92
|
+
**Required somewhere in the HTML (commonly in `<head>`):** the auto-publish marker.
|
|
147
93
|
|
|
148
94
|
```html
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<head>
|
|
152
|
-
<meta charset="utf-8">
|
|
153
|
-
<title>Session 6 — Developer Docs Sources Fixed</title>
|
|
154
|
-
</head>
|
|
155
|
-
<body>
|
|
156
|
-
<!--
|
|
157
|
-
type: session-report
|
|
158
|
-
project: claude-academy
|
|
159
|
-
session: 6
|
|
160
|
-
date: 2026-03-27
|
|
161
|
-
status: completed
|
|
162
|
-
blocked: false
|
|
163
|
-
tags: [session, claude-academy]
|
|
164
|
-
-->
|
|
95
|
+
<!-- @publish-as-gist -->
|
|
96
|
+
```
|
|
165
97
|
|
|
166
|
-
|
|
98
|
+
The marker triggers the PostToolUse hook after Write or Edit — the hook scans the entire HTML for the literal sentinel string and uploads the file to a secret gist, then prints the gist + preview URLs to your tool output. The marker must be the literal comment text exactly; whitespace inside breaks it.
|
|
167
99
|
|
|
168
|
-
|
|
169
|
-
<p>Both Developer Docs notebooks load fully with working sources:</p>
|
|
170
|
-
<ul>
|
|
171
|
-
<li><strong>Notebook #28 — Building with Claude & Tools:</strong> 52 sources loaded (all green)</li>
|
|
172
|
-
<li><strong>Notebook #29 — Agent SDK & Testing:</strong> 49 sources loaded (all green)</li>
|
|
173
|
-
</ul>
|
|
174
|
-
<p><strong>Fix:</strong> Use <code>docs.anthropic.com/en/docs/X</code> URLs (drop the .md extension, swap domain). Tested one URL first, then bulk-loaded.</p>
|
|
100
|
+
**Required at the top of `<body>`:** the frontmatter HTML comment from step 1.
|
|
175
101
|
|
|
176
|
-
|
|
177
|
-
<p>All 10 Audio Overviews are ready to generate but hit the daily limit wall.</p>
|
|
102
|
+
**Required as the first content section:** an opening "What this session shipped" paragraph + bullets — written so a reader with zero prior context understands the outcome. For continuation sessions (where the substantive work landed in a prior session), recap the parent session's outcome briefly so the report stands alone.
|
|
178
103
|
|
|
179
|
-
|
|
180
|
-
<ul>
|
|
181
|
-
<li><strong>Account:</strong> Notebooks live under secondary@example.com (authuser=1), NOT the default primary@example.com.</li>
|
|
182
|
-
<li><strong>Audio budget:</strong> Once the 24h window resets, all 10 overviews fit within the 20/day Pro limit.</li>
|
|
183
|
-
</ul>
|
|
104
|
+
**Required: self-contained HTML.** Embed all styles in a `<style>` block inside `<head>` (inline `style="..."` attributes are also fine if preferred); no `./relative/paths` to local assets. Doc-gist's transport sends a single HTML file with no neighbors, so any relative-path reference (CSS, JS, images) cannot resolve and the asset will be missing from the preview. Absolute external URLs (CDN-hosted assets, image hotlinks) work but add an external dependency the report should not need — design the report to stand alone with no external dependencies.
|
|
184
105
|
|
|
185
|
-
|
|
186
|
-
</html>
|
|
187
|
-
```
|
|
106
|
+
Beyond those four requirements, design the shape that fits. A convergence loop session reads naturally as an incident timeline (`12-incident-report.html`); a feature build reads as a PR writeup (`17-pr-writeup.html`); a research session reads as a feature explainer (`14-research-feature-explainer.html`). Read the matching gallery entry for typography, palette, spatial idioms — adapt, do not copy.
|
|
188
107
|
|
|
189
|
-
|
|
108
|
+
**Write the file** via the Write tool to the vault path. Create the project directory via `mkdir -p` if it does not exist. The auto-publish hook fires after the Write completes and prints a gist + preview URL pair to tool output (the bare preview URL on stdout; both labeled `Gist: <url>` and `Preview: <url>` lines on stderr — both streams appear in the agent's tool-result). Step 3 performs **two Edit calls** and each one re-fires the hook with a fresh URL pair — only the URL pair from the second (final) Step-3 Edit is canonical for the just-created session (the step-2 pair and the first step-3 pair are orphaned the moment the next Edit lands). Always quote the URL pair from the **most recent auto-publish run for the session being created**. Note: Step 5's tidy audits every `.html` file in the project folder including the just-created session, so if Step 5's frontmatter auto-fix touches the current session, the Step 5 republish URL pair becomes the new canonical pair for that session (re-quote it to the user with a "Session republished — new preview: <url>" line). The second Step-3 Edit's URL pair stays canonical only when Step 5 does not touch the current session.
|
|
190
109
|
|
|
191
|
-
|
|
110
|
+
**If the Write fails**, output the HTML content in the conversation so the user can copy it manually. Before emitting the HTML to chat, resolve the `vault_context_retrieved` placeholder to `true` or `false` based on the same vault-MCP-tool scan that Step 3 would have run, and include the matching vault-context `<li>` line (Retrieved or Not retrieved) so the emitted HTML is a valid copy-paste artifact with complete frontmatter. Skip Step 3 and continue at step 4.
|
|
192
111
|
|
|
193
|
-
|
|
112
|
+
## Step 3: Vault Context Tracking
|
|
194
113
|
|
|
195
|
-
|
|
196
|
-
- `mcp__obsidian__search_notes`
|
|
197
|
-
- `mcp__obsidian__read_note`
|
|
198
|
-
- `mcp__obsidian__read_multiple_notes`
|
|
114
|
+
This step runs automatically after step 2.
|
|
199
115
|
|
|
200
|
-
|
|
116
|
+
Review the conversation history for any use of these vault MCP tools (look only at tool calls the session made before /session-log itself ran):
|
|
201
117
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
118
|
+
- `mcp__obsidian__search_notes`
|
|
119
|
+
- `mcp__obsidian__read_note`
|
|
120
|
+
- `mcp__obsidian__read_multiple_notes`
|
|
205
121
|
|
|
206
|
-
|
|
207
|
-
```html
|
|
208
|
-
<h2>⚠️ Notes</h2>
|
|
209
|
-
<ul>
|
|
210
|
-
<li><strong>Vault context:</strong> Not retrieved</li>
|
|
211
|
-
</ul>
|
|
212
|
-
```
|
|
122
|
+
Edit the vault HTML via two Edit calls (each Edit re-fires the auto-publish hook and creates a fresh gist; **the URL pair from the second/final Edit is canonical** — the first Edit's URLs are orphaned the moment the second Edit lands):
|
|
213
123
|
|
|
214
|
-
|
|
124
|
+
1. Set the frontmatter `vault_context_retrieved` field to `true` when any of the three tools fired this session, `false` otherwise.
|
|
125
|
+
2. Append one fact — vault-context status — into whatever section the report designer placed for notes / metadata / references. If the report has no such section, append a fresh `<h2>Notes</h2>` block before `</body>`:
|
|
215
126
|
|
|
216
|
-
|
|
127
|
+
```html
|
|
128
|
+
<h2>Notes</h2>
|
|
129
|
+
<ul>
|
|
130
|
+
<!-- Pick exactly one of the two forms based on whether vault MCP tools fired this session: -->
|
|
131
|
+
<li><strong>Vault context:</strong> Retrieved ([list of note paths])</li>
|
|
132
|
+
<li><strong>Vault context:</strong> Not retrieved</li>
|
|
133
|
+
</ul>
|
|
134
|
+
```
|
|
217
135
|
|
|
218
|
-
|
|
136
|
+
If the report already has a notes / references section, use Edit to insert one matching child element at the end of that section. The element shape mirrors whatever the section already uses: an extra `<li>` before the closing `</ul>` for a list, an extra `<dt>Vault context</dt><dd>…</dd>` pair before the closing `</dl>` for a description list, an extra `<p><strong>Vault context:</strong> …</p>` before the section's closing tag for a paragraph-based section. Pick the form that matches the surrounding markup.
|
|
219
137
|
|
|
220
|
-
|
|
138
|
+
The gist URL stays out of the HTML body on purpose: each Edit re-fires the auto-publish hook and produces a brand-new gist ID, so any URL embedded in the file becomes stale the instant the next Edit lands. The canonical gist + preview URL is the pair from **the most recent auto-publish run that touched the current session report** — typically the second (final) Step-3 Edit, but the Step 5 Edit takes over as canonical when Step 5's frontmatter auto-fix edits the current session (re-quote the Step 5 pair with a "Session [N] republished — new preview: <url>" line per the Step 2 guidance at line 107). Quote whichever pair is most recent when announcing the report.
|
|
221
139
|
|
|
222
|
-
|
|
140
|
+
## Step 4: Decision Extraction
|
|
223
141
|
|
|
224
|
-
|
|
142
|
+
Scan the conversation for decisions, gotchas, or architectural choices that were not already saved via `/remember`. For each one found, ask the user via `AskUserQuestion`:
|
|
225
143
|
|
|
226
|
-
|
|
144
|
+
> "I noticed this decision: [summary]. Save it to the vault via `/remember`?"
|
|
227
145
|
|
|
228
|
-
|
|
146
|
+
Only invoke `/remember` for decisions the user confirms; `/remember` writes the decision as a vault note. If no unrecorded decisions are found, skip silently.
|
|
229
147
|
|
|
230
|
-
|
|
148
|
+
## Step 5: Session Tidy (Project Scope)
|
|
231
149
|
|
|
232
|
-
|
|
150
|
+
Scope: the current project's session folder only.
|
|
233
151
|
|
|
152
|
+
1. **List files** in the project's vault session folder via Bash `ls`.
|
|
234
153
|
2. **Quick audit** each `.html` file for:
|
|
235
154
|
- **Naming convention:** must match `[N]. [Title].html`
|
|
236
|
-
- **Frontmatter completeness:** HTML comment block at top contains `type`, `project`, `session`, `date`, `status`, `blocked`, `tags`
|
|
155
|
+
- **Frontmatter completeness:** HTML comment block at top of `<body>` contains `type`, `project`, `session`, `date`, `status`, `blocked`, `vault_context_retrieved`, `tags`
|
|
237
156
|
- **Status coherence:** `status: completed` with `blocked: true` is contradictory. `status: in-progress` or `status: blocked` on sessions older than 7 days is stale.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
- Missing frontmatter fields that can be inferred (e.g., `blocked: false` when status is `completed`)
|
|
157
|
+
3. **Auto-fix minor issues** via Edit:
|
|
158
|
+
- Missing frontmatter fields that can be inferred (e.g., `blocked: false` when status is `completed`; `vault_context_retrieved: false` when the field is absent, since the field defaults to false in pre-existing sessions)
|
|
241
159
|
- `type` field set to a wrong value (correct to `session-report`)
|
|
242
160
|
|
|
161
|
+
Each Edit on a marked HTML file re-fires doc-gist's auto-publish hook and produces a fresh gist + preview URL pair in the agent's tool output (the bare preview URL on stdout; both labeled `Gist: <url>` and `Preview: <url>` lines on stderr) — the prior gist URL becomes orphaned. Surface a `Session [N] republished — new preview: <url>` line per Edit so the user can update any prior shares.
|
|
243
162
|
4. **Report issues that need user input:**
|
|
244
163
|
- Files with wrong naming convention (propose new name)
|
|
245
164
|
- Stale statuses (propose update to `completed` or ask)
|
|
246
165
|
- Contradictory status/blocked combos
|
|
247
166
|
|
|
248
167
|
If no issues are found, skip silently. Do not report "all clean."
|
|
249
|
-
|
|
250
168
|
5. **Rollup check:** if the project has 5+ sessions and no `Summary.html` or `Summary.md`, mention it:
|
|
251
|
-
> "This project has [N] sessions and no summary.
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## Step 5: Publish via /doc-gist
|
|
256
|
-
|
|
257
|
-
This step runs automatically after Step 4 completes.
|
|
258
|
-
|
|
259
|
-
### 5a. Run gist_upload.py
|
|
260
|
-
|
|
261
|
-
Hand the freshly-written HTML file to `/doc-gist` via its gist_upload.py script. Use the PowerShell tool so quoting handles spaces in the vault path:
|
|
262
|
-
|
|
263
|
-
```powershell
|
|
264
|
-
python "$HOME/.claude/skills/doc-gist/scripts/gist_upload.py" `
|
|
265
|
-
--input "<absolute path to the .html file>" `
|
|
266
|
-
--description "Session [N] — [Title] · session-log · [Project]"
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Replace the bracketed values from Step 1's metadata. The script writes a temp copy, uploads as a secret gist, and prints two URLs to stderr — capture both:
|
|
270
|
-
|
|
271
|
-
- **Preview:** `https://htmlpreview.github.io/?https://gist.githubusercontent.com/...`
|
|
272
|
-
- **Gist:** `https://gist.github.com/...`
|
|
273
|
-
|
|
274
|
-
Quote both URLs back to the user as clickable links.
|
|
275
|
-
|
|
276
|
-
**If gh is not authenticated**, gist_upload.py exits non-zero with the `gh auth login` prompt. Surface that message to the user, skip 5b, and continue with Step 6 — the vault HTML is the canonical artifact. The publish step is a hand-off, not a gate.
|
|
277
|
-
|
|
278
|
-
**If the browser should not open automatically**, append `--no-open`. The gist still publishes; only the auto-open is suppressed.
|
|
169
|
+
> "This project has [N] sessions and no summary. A rollup would help; `/session-tidy` is defined for the Markdown session format and may mis-audit or propose destructive renames against HTML sessions, so a manual rollup is the safe path."
|
|
279
170
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
After 5a succeeds, edit the vault HTML to embed the Preview URL inside the Notes section so future readers of the local file can jump to the published gist. Use the Edit tool with the absolute path from Step 1.
|
|
171
|
+
## Step 6: Finalize
|
|
283
172
|
|
|
284
|
-
|
|
173
|
+
Copy a `/rename` command to the user's clipboard via PowerShell:
|
|
285
174
|
|
|
286
|
-
```html
|
|
287
|
-
<li><strong>Published as:</strong> <a href="<preview URL from 5a>">gist preview</a></li>
|
|
288
175
|
```
|
|
289
|
-
|
|
290
|
-
If the file has no Notes section, append a fresh one before `</body>`:
|
|
291
|
-
|
|
292
|
-
```html
|
|
293
|
-
<h2>⚠️ Notes</h2>
|
|
294
|
-
<ul>
|
|
295
|
-
<li><strong>Published as:</strong> <a href="<preview URL from 5a>">gist preview</a></li>
|
|
296
|
-
</ul>
|
|
176
|
+
pwsh -NoProfile -Command "Set-Clipboard '/rename [Project] - [Primary Outcome]'"
|
|
297
177
|
```
|
|
298
178
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
---
|
|
302
|
-
|
|
303
|
-
## Step 6: Finalize
|
|
304
|
-
|
|
305
|
-
Copy a `/rename` command to the user's clipboard via Bash: `echo -n "/rename [Project] - [Primary Outcome]" | clip.exe`. Then tell the user:
|
|
179
|
+
Then tell the user:
|
|
306
180
|
|
|
307
181
|
> "Copied `/rename [Project] - [Primary Outcome]` to your clipboard. Paste it to rename this session."
|
|
308
182
|
|
|
309
|
-
The primary outcome comes from the session title
|
|
183
|
+
The primary outcome comes from the session title resolved in step 1.
|
|
310
184
|
|
|
311
185
|
---
|
|
312
186
|
|
|
313
|
-
## Best Practices
|
|
314
|
-
|
|
315
|
-
- Each section in the session report is an outcome, not a process step ("Sources Fixed" not "We debugged source loading")
|
|
316
|
-
- Body should be self-contained -- no context needed beyond the note itself
|
|
317
|
-
- If the session was exploratory with no concrete outcome, use 🔧 or 📋 sections to describe what was investigated and what's next
|
|
318
|
-
- Keep it scannable: a reader should grasp the session in 15 seconds from headers alone
|
|
319
|
-
- Tables are powerful -- use them whenever you have structured data (queued work, test results, file inventories)
|
|
320
|
-
- Skip inline styling. Doc-gist's template provides the visual rhythm; semantic HTML (h1/h2/p/ul/table) carries the structure.
|
|
321
|
-
|
|
322
187
|
## Run-and-report checklist
|
|
323
188
|
|
|
324
|
-
Copy and check off:
|
|
325
|
-
|
|
326
189
|
- [ ] Backend detected and announced
|
|
327
|
-
- [ ] Session number resolved from `[N]. *.html` files
|
|
328
|
-
- [ ] HTML
|
|
329
|
-
- [ ]
|
|
190
|
+
- [ ] Session number resolved from `[N]. *.html` and `[N]. *.md` files (both parsed to preserve sequence across the format migration)
|
|
191
|
+
- [ ] HTML composed via doc-gist's shape principles (gallery-anchored)
|
|
192
|
+
- [ ] `<!-- @publish-as-gist -->` marker present somewhere in the HTML
|
|
193
|
+
- [ ] Frontmatter HTML comment present at top of `<body>`
|
|
194
|
+
- [ ] Opening section answers "what shipped / why / impact" for a cold reader
|
|
195
|
+
- [ ] Self-contained HTML (no relative-path asset refs; avoid external dependencies)
|
|
196
|
+
- [ ] Auto-publish URLs captured from step 2 and step 3 (or HTML emitted to chat when step 2 Write failed)
|
|
197
|
+
- [ ] Vault-context line appended via Edit (step 3); URL pair from the most recent auto-publish run for the current session quoted to the user (typically the final Step-3 Edit, but the Step 5 republish takes over when Step 5 edits the current session)
|
|
330
198
|
- [ ] Decision extraction surfaced any unrecorded items
|
|
331
199
|
- [ ] Session tidy reported anomalies or stayed silent
|
|
332
|
-
- [ ]
|
|
333
|
-
- [ ] Preview URL and Gist URL quoted to the user
|
|
334
|
-
- [ ] Preview URL injected back into the vault HTML's Notes section
|
|
335
|
-
- [ ] /rename command copied to clipboard via `clip.exe`
|
|
200
|
+
- [ ] `/rename` command copied to clipboard via `pwsh Set-Clipboard`
|
|
336
201
|
|
|
337
202
|
## Folder map
|
|
338
203
|
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"""Constants shared by grant_project_claude_permissions and revoke_project_claude_permissions."""
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from config.preflight_constants import GIT_DIRECTORY_NAME
|
|
6
|
-
|
|
7
|
-
__all__ = (
|
|
8
|
-
"ALL_PERMISSION_ALLOW_TOOLS",
|
|
9
|
-
"AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE",
|
|
10
|
-
"CLAUDE_SETTINGS_DIRECTORY_NAME",
|
|
11
|
-
"CLAUDE_SETTINGS_FILENAME",
|
|
12
|
-
"GIT_DIRECTORY_NAME",
|
|
13
|
-
"TEXT_FILE_ENCODING",
|
|
14
|
-
"UNIQUE_TEMPORARY_SUFFIX_BYTE_LENGTH",
|
|
15
|
-
"get_claude_user_settings_path",
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ALL_PERMISSION_ALLOW_TOOLS: tuple[str, ...] = ("Edit", "Write", "Read")
|
|
20
|
-
|
|
21
|
-
AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE: str = (
|
|
22
|
-
"Trusted local workspace: {project_path}/.claude/** is the user's "
|
|
23
|
-
"project Claude Code config tree; edits inside are routine"
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
CLAUDE_SETTINGS_DIRECTORY_NAME: str = ".claude"
|
|
27
|
-
|
|
28
|
-
CLAUDE_SETTINGS_FILENAME: str = "settings.json"
|
|
29
|
-
|
|
30
|
-
TEXT_FILE_ENCODING: str = "utf-8"
|
|
31
|
-
|
|
32
|
-
UNIQUE_TEMPORARY_SUFFIX_BYTE_LENGTH: int = 8
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def get_claude_user_settings_path() -> Path:
|
|
36
|
-
return Path.home() / CLAUDE_SETTINGS_DIRECTORY_NAME / CLAUDE_SETTINGS_FILENAME
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"""Configuration constants for the pr_description_enforcer PreToolUse hook."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
_PLUGIN_ROOT: str = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
8
|
-
PR_GUIDE_PATH: str = os.path.join(_PLUGIN_ROOT, "docs", "PR_DESCRIPTION_GUIDE.md")
|
|
9
|
-
|
|
10
|
-
MINIMUM_SUBSTANTIVE_PROSE_CHARS: int = 40
|
|
11
|
-
|
|
12
|
-
FENCED_CODE_BLOCK_PATTERN: re.Pattern[str] = re.compile(r"```.*?```", re.DOTALL)
|
|
13
|
-
INLINE_CODE_PATTERN: re.Pattern[str] = re.compile(r"`[^`]*`")
|
|
14
|
-
HEADING_LINE_PATTERN: re.Pattern[str] = re.compile(r"^#+[ \t].*$", re.MULTILINE)
|
|
15
|
-
BOLD_PAIR_PATTERN: re.Pattern[str] = re.compile(r"\*\*([^*]+?)\*\*")
|
|
16
|
-
BULLET_MARKER_PATTERN: re.Pattern[str] = re.compile(r"^\s*[-*+]\s+", re.MULTILINE)
|
|
17
|
-
BLOCKQUOTE_MARKER_PATTERN: re.Pattern[str] = re.compile(r"^\s*>\s+", re.MULTILINE)
|
|
18
|
-
LINK_TEXT_PATTERN: re.Pattern[str] = re.compile(r"\[([^\]]+)\]\([^)]+\)")
|
|
19
|
-
WHITESPACE_RUN_PATTERN: re.Pattern[str] = re.compile(r"\s+")
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"""Behavior tests for pr_description_enforcer_constants module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
import sys
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
_HOOKS_ROOT = Path(__file__).resolve().parent.parent
|
|
10
|
-
if str(_HOOKS_ROOT) not in sys.path:
|
|
11
|
-
sys.path.insert(0, str(_HOOKS_ROOT))
|
|
12
|
-
|
|
13
|
-
from config import pr_description_enforcer_constants as constants_module
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def test_plugin_root_is_private_module_attribute() -> None:
|
|
17
|
-
assert hasattr(constants_module, "_PLUGIN_ROOT")
|
|
18
|
-
assert isinstance(constants_module._PLUGIN_ROOT, str)
|
|
19
|
-
assert os.path.isabs(constants_module._PLUGIN_ROOT)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_plugin_root_public_name_is_not_exported() -> None:
|
|
23
|
-
assert not hasattr(constants_module, "PLUGIN_ROOT")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def test_pr_guide_path_resolves_under_plugin_root_docs() -> None:
|
|
27
|
-
expected_pr_guide_path = os.path.join(
|
|
28
|
-
constants_module._PLUGIN_ROOT, "docs", "PR_DESCRIPTION_GUIDE.md"
|
|
29
|
-
)
|
|
30
|
-
assert constants_module.PR_GUIDE_PATH == expected_pr_guide_path
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_minimum_substantive_prose_chars_is_positive_integer() -> None:
|
|
34
|
-
assert isinstance(constants_module.MINIMUM_SUBSTANTIVE_PROSE_CHARS, int)
|
|
35
|
-
assert constants_module.MINIMUM_SUBSTANTIVE_PROSE_CHARS > 0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_fenced_code_block_pattern_matches_triple_backtick_block() -> None:
|
|
39
|
-
sample_markdown = "before ```python\ncode\n``` after"
|
|
40
|
-
match = constants_module.FENCED_CODE_BLOCK_PATTERN.search(sample_markdown)
|
|
41
|
-
assert match is not None
|
|
42
|
-
assert match.group(0).startswith("```")
|
|
43
|
-
assert match.group(0).endswith("```")
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def test_inline_code_pattern_matches_single_backtick_span() -> None:
|
|
47
|
-
match = constants_module.INLINE_CODE_PATTERN.search("see `value` here")
|
|
48
|
-
assert match is not None
|
|
49
|
-
assert match.group(0) == "`value`"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def test_heading_line_pattern_matches_atx_heading() -> None:
|
|
53
|
-
match = constants_module.HEADING_LINE_PATTERN.search("## Description\n")
|
|
54
|
-
assert match is not None
|
|
55
|
-
assert match.group(0).strip() == "## Description"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def test_bold_pair_pattern_captures_inner_text() -> None:
|
|
59
|
-
match = constants_module.BOLD_PAIR_PATTERN.search("this is **bold** text")
|
|
60
|
-
assert match is not None
|
|
61
|
-
assert match.group(1) == "bold"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def test_bullet_marker_pattern_strips_dash_bullet_from_line() -> None:
|
|
65
|
-
stripped_line = constants_module.BULLET_MARKER_PATTERN.sub("", "- first item")
|
|
66
|
-
assert stripped_line == "first item"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def test_blockquote_marker_pattern_strips_quote_marker_from_line() -> None:
|
|
70
|
-
stripped_line = constants_module.BLOCKQUOTE_MARKER_PATTERN.sub("", "> quoted line")
|
|
71
|
-
assert stripped_line == "quoted line"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def test_link_text_pattern_captures_anchor_text() -> None:
|
|
75
|
-
match = constants_module.LINK_TEXT_PATTERN.search("See [the docs](https://example.com) now")
|
|
76
|
-
assert match is not None
|
|
77
|
-
assert match.group(1) == "the docs"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def test_whitespace_run_pattern_collapses_multiple_spaces() -> None:
|
|
81
|
-
collapsed_text = constants_module.WHITESPACE_RUN_PATTERN.sub(" ", "a b\t\tc\n\nd")
|
|
82
|
-
assert collapsed_text == "a b c d"
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""Configuration constants for claude_permissions_common shared helpers."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
TEXT_FILE_ENCODING: str = "utf-8"
|
|
6
|
-
ALL_PERMISSION_ALLOW_TOOLS: tuple[str, ...] = ("Edit", "Write", "Read")
|
|
7
|
-
AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE: str = (
|
|
8
|
-
"Trusted local workspace: {project_path}/.claude/** is the user's "
|
|
9
|
-
"project Claude Code config tree; edits inside are routine"
|
|
10
|
-
)
|
|
11
|
-
ATOMIC_WRITE_TEMPORARY_SUFFIX: str = ".tmp"
|
|
12
|
-
GIT_DIRECTORY_MARKER: str = ".git"
|
|
13
|
-
CLAUDE_DIRECTORY_MARKER: str = ".claude"
|
|
14
|
-
CLAUDE_USER_SETTINGS_FILENAME: str = "settings.json"
|
|
15
|
-
DEFAULT_SETTINGS_FILE_MODE: int = 0o600
|
|
16
|
-
SETTINGS_PERMISSIONS_KEY: str = "permissions"
|
|
17
|
-
SETTINGS_ALLOW_KEY: str = "allow"
|
|
18
|
-
SETTINGS_ADDITIONAL_DIRECTORIES_KEY: str = "additionalDirectories"
|
|
19
|
-
SETTINGS_AUTO_MODE_KEY: str = "autoMode"
|
|
20
|
-
SETTINGS_ENVIRONMENT_KEY: str = "environment"
|