diff --git a/.deepreview b/.deepreview index 40945f75..8e622fc3 100644 --- a/.deepreview +++ b/.deepreview @@ -131,13 +131,33 @@ requirements_traceability: false confidence (a passing test that doesn't actually verify anything) or wastes reviewer judgment on something a machine can check exactly. + **Use anonymous DeepSchemas** (`.deepschema..yml`) when + requirements target a specific file — whether structural or semantic: + - "This config file MUST include a timeout field" — structural check + for one file (use `json_schema_path` or `verification_bash_command` + for exact verification) + - "The learn workflow MUST accept X and Y step arguments" — the + requirement governs a specific YAML file's content + - "Skill MUST instruct the agent to do X" — judgment-based check + of prose in one specific file + - "The error message MUST include a suggestion for how to fix the + problem" — governs a specific source file's behavior + + Anonymous DeepSchemas provide both write-time validation and review-time + checks, and they keep the requirement co-located with the file it governs. + **Prefer them over both tests and `.deepreview` rules whenever the + requirement targets a specific file** rather than a class of files. + DeepSchemas can enforce structural requirements via `json_schema_path` + or `verification_bash_command` just as precisely as a test, while also + supporting judgment-based requirements in the same schema. + **Use automated tests** (`tests/`) when the requirement specifies a - concrete, machine-verifiable fact: - - File exists at a specific path - - JSON/YAML field has a specific value - - Config contains a specific identifier (e.g., `mcp__deepwork__get_review_instructions`) + concrete, machine-verifiable fact that spans multiple files or is not + tied to a single file's content: - File A is byte-identical to file B - - A data structure has a required shape + - A Python function returns the correct value for given inputs + - A CLI command produces expected output + - A data structure assembled from multiple sources has a required shape Tests reference requirement IDs via docstrings and traceability comments. @@ -150,21 +170,6 @@ requirements_traceability: - "Documentation MUST stay in sync with code" — are the descriptions still accurate after changes? - **Use anonymous DeepSchemas** (`.deepschema..yml`) when the - requirement is specific to a single file's behavior or content: - - "The error message in situation X MUST include a suggestion for how - to fix the problem" — place the requirement in a `.deepschema` for - the file that implements that functionality - - "This config file MUST include a timeout field" — a structural - requirement for one specific file - - "Skill MUST instruct the agent to do X" — does the prose in this - specific skill file convey X clearly enough? - - Anonymous DeepSchemas provide both write-time validation and review-time - checks, and they keep the requirement co-located with the file it governs. - Prefer them over `.deepreview` rules whenever the requirement targets a - specific file rather than a class of files. - Both `.deepreview` rules and DeepSchemas reference requirement IDs in their `description`, `instructions`, or `requirements` fields. @@ -456,7 +461,7 @@ deepreview_config_quality: and a specific recommendation. job_schema_instruction_compatibility: - description: "Verify deepwork_jobs job.yml inline instructions are compatible with the job schema." + description: "Verify all standard and library job.yml definitions and templates are compatible with the job schema." match: include: - "src/deepwork/jobs/job.schema.json" diff --git a/.github/workflows/claude-code-test.yml b/.github/workflows/claude-code-test.yml index 24614647..8aced0d2 100644 --- a/.github/workflows/claude-code-test.yml +++ b/.github/workflows/claude-code-test.yml @@ -206,7 +206,7 @@ jobs: if: steps.check-key.outputs.has_key == 'true' run: | # Create a fresh project with NO pre-existing job definitions - mkdir -p test_project/.claude/skills/deepwork + mkdir -p test_project/.claude cd test_project git init @@ -216,16 +216,14 @@ jobs: git add . && git commit -m "init" cd .. - # Copy plugin skill into the test project (replaces old `deepwork install`) - cp plugins/claude/skills/deepwork/SKILL.md test_project/.claude/skills/deepwork/ - - # Write MCP config using bare `deepwork` command (CI has it on PATH - # via .venv/bin; the plugin's .mcp.json uses `uvx` which isn't available here) + # The plugin (--plugin-dir) provides skills, hooks, and MCP server config. + # Override the plugin's MCP config to use the bare `deepwork` command + # (the plugin uses `uvx` which may not resolve the local venv install). python3 -c " import json mcp = {'mcpServers': {'deepwork': { 'command': 'deepwork', - 'args': ['serve', '--path', '.', '--external-runner', 'claude'] + 'args': ['serve', '--path', '.', '--platform', 'claude'] }}} with open('test_project/.mcp.json', 'w') as f: json.dump(mcp, f, indent=2) @@ -240,7 +238,8 @@ jobs: 'Bash(*)', 'Read(./**)', 'Edit(./**)', 'Write(./**)', 'Skill(*)', 'mcp__deepwork__get_workflows', 'mcp__deepwork__start_workflow', 'mcp__deepwork__finished_step', 'mcp__deepwork__abort_workflow', - 'mcp__deepwork__go_to_step' + 'mcp__deepwork__go_to_step', + 'mcp__deepwork__mark_review_as_passed' ] } } @@ -249,8 +248,8 @@ jobs: " echo "Fresh test project setup complete" - echo "Available skills:" - ls -la test_project/.claude/skills/ + echo "MCP config:" + cat test_project/.mcp.json # STEP 1: Use /deepwork to CREATE the fruits job via MCP workflow # @@ -269,7 +268,7 @@ jobs: # Use --debug and --output-format stream-json for diagnosing failures. # stream-json shows every tool call; output is captured to a file for the failure handler. set -o pipefail - claude --print --verbose --output-format stream-json --max-turns 20 --debug --model claude-sonnet-4-6 --dangerously-skip-permissions <<'PROMPT_EOF' | tee ../claude-create-job.jsonl + claude --print --verbose --output-format stream-json --max-turns 25 --debug --model claude-sonnet-4-6 --dangerously-skip-permissions --plugin-dir "$GITHUB_WORKSPACE/plugins/claude" <<'PROMPT_EOF' | tee ../claude-create-job.jsonl /deepwork I want to create a simple job called "fruits" for identifying and classifying fruits. Here are the EXACT specifications. @@ -380,7 +379,7 @@ jobs: echo "=== Running fruits workflow with test input via /deepwork ===" set -o pipefail - claude --print --verbose --output-format stream-json --max-turns 20 --debug --model claude-sonnet-4-6 --dangerously-skip-permissions <<'PROMPT_EOF' | tee ../claude-run-workflow.jsonl + claude --print --verbose --output-format stream-json --max-turns 25 --debug --model claude-sonnet-4-6 --dangerously-skip-permissions --plugin-dir "$GITHUB_WORKSPACE/plugins/claude" <<'PROMPT_EOF' | tee ../claude-run-workflow.jsonl /deepwork Run the fruits full workflow. Process the list to the file and don't give any extra commentary or text output. NEVER use AskUserQuestion — you already have all the information you need. You MUST complete all tool calls needed. Do not stop early. diff --git a/AGENTS.md b/AGENTS.md index 4c5da9b7..b1f0d35e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -184,6 +184,10 @@ Each step: 7. **Succinctness**: Jobs, documentation, and code MUST be succinct. Avoid verbose preambles, redundant explanations, and duplicated content. Step instructions should contain only what the agent needs to act — not philosophy, not quality criteria already enforced by the workflow runtime, and not domain tables already in `common_job_info`. If it can be said in one sentence, do not use three. 8. **Documentation Sync**: When making implementation changes, update `doc/architecture.md` and `README.md` to reflect those changes. +## MCP Tool Naming in This Repo + +This repo has **two** MCP server instances: one from the plugin (`plugin:deepwork:deepwork`, tools prefixed `mcp__plugin_deepwork_deepwork__`) and one from the project-level `.mcp.json` (`deepwork`, tools prefixed `mcp__deepwork__`). **Always use the non-plugin prefix** (`mcp__deepwork__*`) when calling MCP tools in this repo — including in the e2e CI test (`claude-code-test.yml`), workflow prompts, and any scripted Claude sessions. The non-plugin server is configured for development/testing (bare `deepwork` command, `--path .`), while the plugin server uses `uvx` and is meant for end-user installations. Note: `how_to_invoke` in `src/deepwork/jobs/mcp/tools.py` hardcodes the `mcp__plugin_deepwork_deepwork__` prefix — this is correct for end-user plugin installations but causes confusion when both servers are present in development. + ## Appendix: Project Structure ``` diff --git a/doc/architecture.md b/doc/architecture.md index 029063ec..dc447ba3 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -483,7 +483,7 @@ DeepWork includes a built-in job called `deepwork_jobs` for managing jobs. It pr - **`repair`** workflow: `fix_settings` → `fix_jobs` → `errata` - Cleans up and migrates DeepWork configurations from prior versions - **`learn`** workflow: `learn` - - Analyzes conversation history to improve job instructions and capture learnings + - Analyzes conversation history to improve job instructions, capture learnings, and create preventive automation (DeepSchemas and DeepReview rules) These are auto-discovered at runtime by the MCP server from the Python package. @@ -532,7 +532,7 @@ User: /deepwork new_job ### The `learn` Workflow -Analyzes conversation history to improve job instructions and capture learnings: +Analyzes conversation history to improve job instructions, capture learnings, and create preventive automation: ``` User: /deepwork_jobs.learn @@ -551,10 +551,14 @@ Claude: I'll analyze this conversation for DeepWork job executions... Bespoke learnings captured: ✓ Created AGENTS.md with project-specific notes about this competitive research instance + Prevention opportunities evaluated: + ✓ Created DeepSchema for competitor_profiles/ output format + ✓ Added DeepReview rule to enforce source prioritization in research steps + Job instructions updated in place. Changes take effect on next workflow run. ``` -This standalone skill can be run anytime after executing a job to capture learnings and improve instructions. +This standalone skill can be run anytime after executing a job to capture learnings, improve instructions, and create preventive automation (DeepSchemas and DeepReview rules). ### Step Instructions at Runtime @@ -742,6 +746,7 @@ The `/deepwork_jobs.define` command: The `/deepwork_jobs.learn` command: 1. Identifies doc spec-related learnings (quality criteria issues, structure changes) 2. Updates doc spec files with improvements +3. Evaluates prevention opportunities and creates DeepSchemas and DeepReview rules See `doc/doc-specs.md` for complete documentation. diff --git a/plugins/claude/skills/deepschema/SKILL.md b/plugins/claude/skills/deepschema/SKILL.md index bab125ca..7d4291d0 100644 --- a/plugins/claude/skills/deepschema/SKILL.md +++ b/plugins/claude/skills/deepschema/SKILL.md @@ -51,11 +51,11 @@ matchers: - "src/configs/**/*.json" requirements: - has-version: "Every config file MUST include a version field." + # Semantic rules only — structural constraints go in config.schema.json documented-fields: "All fields SHOULD have inline comments explaining their purpose." no-secrets: "Config files MUST NOT contain secrets or credentials." -# Optional: structural validation +# Structural validation — enforce types, required fields, enums, etc. here json_schema_path: "config.schema.json" # Optional: custom validation commands (file path passed as $1) @@ -72,13 +72,69 @@ Place a `.deepschema..yml` file next to the target file: ```yaml requirements: api-key-rotated: "The API key MUST be rotated every 90 days." - format-valid: "The file MUST be valid YAML." + no-plaintext-secrets: "Credentials MUST use environment variable references, not literal values." # Reference a named schema for shared requirements parent_deep_schemas: - api_endpoint ``` +## JSON Schema First: Maximize Structural Validation + +**The `json_schema_path` file is the primary enforcement mechanism.** Every constraint that _can_ be expressed structurally MUST go in the JSON Schema, not in requirements. Requirements exist only for semantic rules that JSON Schema cannot express. + +Put in the JSON Schema (not requirements): +- File format validity (valid JSON, valid YAML) +- Field types (string, number, boolean, array, object) +- Required fields +- Allowed property names (`additionalProperties: false`) +- Enum values and allowed constants +- Array item types and constraints (`minItems`, `uniqueItems`) +- Numeric ranges (`minimum`, `maximum`) +- String patterns (`pattern`, `format`) +- Conditional field presence (`if`/`then` — e.g., "when type is 'http', url is required") +- Nested object shapes and their constraints + +Put in requirements (not the JSON Schema): +- Semantic rules about _meaning_ ("secrets MUST NOT appear in shared settings") +- Cross-file concerns ("this field MUST reference an existing named schema") +- Behavioral gotchas ("sandbox paths use different prefix semantics than permission paths") +- Design guidance ("deny rules SHOULD be used for hard security boundaries, not soft preferences") +- Anything requiring judgment or context a machine validator cannot assess + +**Build the JSON Schema to be as strict and comprehensive as possible.** Use `additionalProperties: false` to catch typos. Use enums for closed sets. Use `if/then` for conditional requirements. Use `pattern` for string formats. Use `$defs` and `$ref` for reusable types. Use `anyOf` for discriminated unions. Use `uniqueItems`, `minLength`, `minItems` where appropriate. A good JSON Schema catches errors at write time before a reviewer ever sees the file. Requirements that duplicate what the schema already enforces are noise — they dilute the reviewer's attention and risk contradicting the schema. + +### Verification Commands for Non-JSON Files + +For files that aren't JSON or YAML (markdown, shell scripts, plain text, custom formats), `verification_bash_command` serves the same role as `json_schema_path` — it's the primary structural enforcement mechanism. The same principle applies: anything a command can check exactly MUST go in a verification command, not in requirements. + +```yaml +# Example: RFC 2119 requirements files (markdown) +verification_bash_command: + - "grep -nE '^[0-9]+\\.' \"$1\" | grep -vE 'MUST|SHALL|SHOULD|MAY|REQUIRED|RECOMMENDED|OPTIONAL' | { if read -r line; then echo \"FAIL: Requirement without RFC 2119 keyword: $line\"; exit 1; fi; }" + +requirements: + # Only semantic rules the command can't check + testability: "Each requirement MUST be specific enough to be verifiable." +``` + +Commands receive the file path as `$1`, must exit 0 on success and non-zero on failure, and have a 30-second timeout. + +### Check SchemaStore for Existing Schemas + +Before writing a JSON Schema from scratch, check whether a published schema already exists at [SchemaStore](https://www.schemastore.org/) (`https://json.schemastore.org/.json`). SchemaStore hosts community-maintained schemas for hundreds of config file formats. + +If a good schema exists: +1. **Vendor a local copy** into your schema directory (e.g., `claude_settings.schema.json`) +2. **Add a `_source` field** at the top of the file with the original URL and sync date: + ```json + { + "_source": "Vendored from https://json.schemastore.org/example.json. To update: fetch the latest version from that URL and replace this file. Last synced: 2026-04-01." + } + ``` +3. **Point `json_schema_path`** at the local copy — this avoids network dependencies during validation +4. **Periodically re-fetch** the upstream schema to pick up improvements — the `_source` field tells future maintainers where to look + ## Schema Fields Reference | Field | Description | diff --git a/specs/deepwork/DW-REQ-011-deepschema.md b/specs/deepwork/DW-REQ-011-deepschema.md index b8175914..3bcfd00d 100644 --- a/specs/deepwork/DW-REQ-011-deepschema.md +++ b/specs/deepwork/DW-REQ-011-deepschema.md @@ -1,5 +1,7 @@ # DW-REQ-011: DeepSchema System +## Overview + The DeepSchema system provides rich, file-level schemas with automatic validation on writes and synthetic review rule generation. ## DW-REQ-011.1: Schema Types @@ -42,13 +44,13 @@ The DeepSchema system provides rich, file-level schemas with automatic validatio 1. A file MUST match a named schema if any of the schema's `matchers` glob patterns match the file's project-relative path. 2. A file MUST match an anonymous schema if a `.deepschema..yml` file exists alongside it. -3. The `get_schemas_for_file_fast()` function MUST avoid full tree walks — it MUST only scan named schema folders and check for the anonymous schema file at O(1). +3. The `get_schemas_for_file_fast()` function MUST avoid full tree walks by only scanning named schema folders and checking for the anonymous schema file at O(1). ## DW-REQ-011.7: Write Hook (PostToolUse) 1. The write hook MUST fire on PostToolUse events for Write and Edit tools. 2. For each applicable schema, the hook MUST inject a conformance note: "Note: this file must conform to the DeepSchema at ``". -3. If `json_schema_path` is set, the hook MUST validate the written file against the JSON Schema. YAML files (`.yml`/`.yaml`) MUST be parsed as YAML before validation. +3. If `json_schema_path` is set, the hook MUST validate the written file against the JSON Schema, parsing YAML files (`.yml`/`.yaml`) as YAML before validation. 4. If `verification_bash_command` is set, the hook MUST execute each command with the file path as `$1`, with a 30-second timeout. 5. Validation failures MUST be reported via `hookSpecificOutput.additionalContext` so the agent can act on them. 6. The hook MUST NOT use `systemMessage` for validation output — that route is user-visible only. @@ -61,10 +63,17 @@ The DeepSchema system provides rich, file-level schemas with automatic validatio 4. Anonymous schema reviews MUST include only the requirements. 5. All generated reviews MUST use the `"individual"` strategy (one file at a time). 6. Generated reviews MUST be included in both `/review` runs and workflow quality gate checks. -7. Review instructions MUST specify RFC 2119 severity logic: reviewers MUST fail any violation of a MUST requirement, MUST fail any SHOULD requirement that could easily be followed but is not, SHOULD give feedback without failing on other applicable items, and MUST ignore requirements that are not applicable. +7. Review instructions MUST specify RFC 2119 severity logic: fail any violation of a MUST requirement, fail any SHOULD requirement that could easily be followed but is not, give feedback without failing on other applicable items, and ignore requirements that are not applicable. ## DW-REQ-011.9: MCP Tool — get_named_schemas 1. The `get_named_schemas` MCP tool MUST return all discovered named schemas. 2. Each entry MUST include `name`, `summary`, and `matchers` fields. 3. Schemas that fail to parse MUST still appear in the results with an error summary instead of a real summary. + +## DW-REQ-011.10: Requirement Quality Constraints + +1. Each requirement in the `requirements` field MUST be verifiable by examining files on the filesystem. +2. Requirements about processes, user behavior, or context not present in files SHOULD be placed in the `instructions` section instead. +3. Requirements MUST NOT restate constraints that are already enforced by the schema's `json_schema_path` or `verification_bash_command`, including syntactic validity (e.g., "must be valid JSON"), field types, allowed enum values, required fields, and structural shape. +4. Requirements SHOULD focus on semantic rules, behavioral gotchas, and cross-field concerns that JSON Schema cannot express. diff --git a/specs/deepwork/jobs/JOBS-REQ-012-learn-workflow.md b/specs/deepwork/jobs/JOBS-REQ-012-learn-workflow.md new file mode 100644 index 00000000..03900f5c --- /dev/null +++ b/specs/deepwork/jobs/JOBS-REQ-012-learn-workflow.md @@ -0,0 +1,30 @@ +# JOBS-REQ-012: Learn Workflow + +## Overview + +The `learn` workflow in the `deepwork_jobs` standard job analyzes conversation history to extract learnings from DeepWork job executions. It improves job instructions with generalizable insights, captures run-specific learnings in AGENTS.md files, and creates preventive automation (DeepSchemas and DeepReview rules) to prevent recurring issues. + +## Requirements + +### JOBS-REQ-012.1: Learning Classification + +1. The learn workflow MUST classify each identified learning as either **generalizable** (applicable to future runs of the same job) or **bespoke** (specific to the current run/context). +2. Generalizable learnings MUST be applied to job instruction files. +3. Bespoke learnings MUST be captured in an AGENTS.md file in the deepest common folder that would contain all future work on the topic. + +### JOBS-REQ-012.2: Prevention Opportunity Evaluation + +1. The learn workflow MUST evaluate whether DeepSchemas or DeepReview rules could prevent issues encountered during the session. +2. If prevention opportunities exist, the workflow SHOULD create the corresponding DeepSchemas or DeepReview rules. +3. If no prevention opportunities are found, the workflow MUST state why none were identified. + +### JOBS-REQ-012.3: Step Arguments + +1. The learn workflow MUST accept a `deepschemas` step argument for outputting created DeepSchema files. +2. The learn workflow MUST accept a `deepreviews` step argument for outputting created DeepReview rule files. + +### JOBS-REQ-012.4: Process Requirements + +1. The workflow MUST enforce that generalizable learnings are applied to job instructions ("Generalizable Learnings Applied"). +2. The workflow MUST enforce that bespoke learnings are captured in AGENTS.md ("Bespoke Learnings Captured"). +3. The workflow MUST enforce that prevention opportunities are evaluated ("Prevention Opportunities Evaluated"). diff --git a/src/deepwork/standard_jobs/deepwork_jobs/.deepschema.job.yml.yml b/src/deepwork/standard_jobs/deepwork_jobs/.deepschema.job.yml.yml new file mode 100644 index 00000000..b22376c1 --- /dev/null +++ b/src/deepwork/standard_jobs/deepwork_jobs/.deepschema.job.yml.yml @@ -0,0 +1,21 @@ +requirements: + learn-workflow-step-arguments: > + The learn workflow MUST accept `deepschemas` and `deepreviews` step + arguments for outputting created DeepSchema and DeepReview rule files. + (JOBS-REQ-012.3) + + learn-workflow-classification: > + The learn workflow's step instructions MUST describe how to classify + each learning as either generalizable (applied to job instructions) or + bespoke (captured in AGENTS.md). (JOBS-REQ-012.1) + + learn-workflow-prevention-evaluation: > + The learn workflow MUST include instructions for evaluating whether + DeepSchemas or DeepReview rules could prevent issues encountered during + the session, and MUST require the agent to state why if none are found. + (JOBS-REQ-012.2) + + learn-workflow-process-requirements: > + The learn workflow's quality review MUST enforce "Generalizable Learnings + Applied", "Bespoke Learnings Captured", and "Prevention Opportunities + Evaluated" as process requirements. (JOBS-REQ-012.4) diff --git a/src/deepwork/standard_jobs/deepwork_jobs/job.yml b/src/deepwork/standard_jobs/deepwork_jobs/job.yml index 546ca395..a35928e0 100644 --- a/src/deepwork/standard_jobs/deepwork_jobs/job.yml +++ b/src/deepwork/standard_jobs/deepwork_jobs/job.yml @@ -49,6 +49,14 @@ step_arguments: description: "Scripts to run parts of the job more efficiently" type: file_path + - name: deepschemas + description: "DeepSchema files (named or anonymous) created to prevent recurring issues" + type: file_path + + - name: deepreviews + description: "DeepReview rules (.deepreview files) created to catch issues during code review" + type: file_path + - name: settings.json description: "Cleaned up Claude settings file with legacy permissions removed" type: file_path @@ -1671,7 +1679,7 @@ workflows: "MCP Server Entry Removed": "The `deepwork serve` entry MUST be removed from `.mcp.json` (or the file deleted if empty)." learn: - summary: "Analyze conversation history to improve job instructions and capture learnings" + summary: "Analyze conversation history to improve job instructions, capture learnings, and create preventive automation" common_job_info_provided_to_all_steps_at_runtime: | Core commands for managing DeepWork jobs. These commands help you define new multi-step workflows, test them on real use cases, and learn from running them. @@ -1679,6 +1687,8 @@ workflows: The `learn` skill reflects on conversations where DeepWork jobs were run, identifies confusion or inefficiencies, and improves job instructions. It also captures bespoke learnings specific to the current run into AGENTS.md files in the working folder. + Additionally, it evaluates whether deepschemas or deepreview rules could prevent + issues encountered during the session — turning one-time fixes into lasting automation. ## Job Schema Reference @@ -1742,6 +1752,13 @@ workflows: - Efficient approaches worth preserving - Good examples that could be added to instructions + 5. **Prevention opportunities** (deepschemas and deepreviews) + - Files that had structural or formatting issues a schema could validate + - Classes of bugs or mistakes a review rule could catch automatically + - Consistency problems across files of the same type + - Patterns the agent repeated incorrectly until corrected + - If the entire session was about fixing an issue, could a schema or review have prevented it? + ### Step 3: Classify Learnings For each learning identified, determine if it is: @@ -1766,6 +1783,17 @@ workflows: - "This project uses camelCase for function names" - "The main config file is at `config/settings.yml`" + **Preventable** (should become a deepschema or deepreview rule): + - Structural or format requirements on specific files → **anonymous deepschema** (`.deepschema..yml` next to the file) + - Structural or format requirements on a category of files → **named deepschema** (`.deepwork/schemas//deepschema.yml` with glob matchers) + - Cross-file consistency checks, style enforcement, or patterns that should be reviewed on every PR → **deepreview rule** (`.deepreview` file with match patterns and review instructions) + - Look at both the process (mistakes the agent made, corrections the user gave) AND the substance (what the whole session was about — if it was fixing a bug, could automation have caught it?) + - Examples: + - Agent kept producing YAML with wrong field order → anonymous deepschema on that file with requirements + - Config files across the project lack version fields → named deepschema for `**/*.config.yml` + - Session was fixing a security issue in auth code → deepreview rule on `src/auth/**` to check for that class of issue + - Migration files keep breaking because they're not tested → deepreview rule on `migrations/**` + ### Step 4: Update Job Instructions (Generalizable Learnings) For each generalizable learning: @@ -1854,6 +1882,43 @@ workflows: 4. **Reference from instructions** - Update the relevant step instruction files to reference the new scripts so future runs use them. + ### Step 7: Create DeepSchemas and DeepReview Rules (Preventable Learnings) + + For each learning classified as **preventable** in Step 3, create the appropriate automation: + + #### Anonymous DeepSchemas (single-file requirements) + + When a specific file had structural or content issues: + 1. Create `.deepschema..yml` in the same directory as the file + 2. Add `requirements` using RFC 2119 keywords (MUST/SHOULD/MAY) + 3. Optionally add `json_schema_path` for structural validation + 4. Optionally inherit from a named schema via `parent_deep_schemas` + + #### Named DeepSchemas (category-wide requirements) + + When a class of files shares requirements: + 1. Create `.deepwork/schemas//deepschema.yml` + 2. Define `matchers` with glob patterns for the files + 3. Add `requirements`, `instructions`, and optional `json_schema_path` + 4. Check if an existing named schema already covers this — extend it rather than duplicating + + #### DeepReview Rules (review-time enforcement) + + When issues should be caught during code review: + 1. Add rules to the nearest `.deepreview` file (create one if needed) + 2. Choose the right strategy: + - `individual` — per-file review (style, structure, correctness) + - `matches_together` — cross-file consistency (versions in sync, naming conventions) + - `all_changed_files` — tripwire reviews (security-sensitive areas) + 3. Write focused review instructions — the reviewer only sees the matched files, so be specific about what to check + 4. For lengthy instructions, extract to `.deepwork/review/.md` and reference via `instructions: { file: ... }` + + #### Verify your changes + + After creating schemas or review rules, verify they work: + - For deepschemas: check that the file exists and the YAML is valid + - For deepreview rules: call `mcp__deepwork__get_review_instructions` to confirm rules are discovered + ## File Reference Patterns When adding entries to AGENTS.md, prefer these patterns: @@ -1923,9 +1988,17 @@ workflows: - From conversation about: Initial competitive analysis run ``` + 3. Created `.deepschema.competitor_list.md.yml` (anonymous schema): + - Requirement: Each competitor entry MUST include a pricing section + - Prevents the missing-pricing issue from recurring on future runs + + **Prevention Opportunities** + + - No deepreview rules needed — the issues were specific to job instructions and file structure, not cross-file consistency. + **Summary** - Updated job instructions and created AGENTS.md with bespoke learnings. + Updated job instructions, created AGENTS.md, and added a deepschema to enforce pricing data in competitor files. ``` ## Handling Edge Cases @@ -1962,6 +2035,10 @@ workflows: required: true scripts: required: false + deepschemas: + required: false + deepreviews: + required: false process_requirements: "Conversation Analyzed": "The agent MUST review the conversation for DeepWork job executions." "Confusion Identified": "The agent MUST identify points of confusion, errors, or inefficiencies." @@ -1971,6 +2048,7 @@ workflows: "Bespoke Learnings Captured": "Run-specific learnings MUST be added to AGENTS.md." "File References Used": "AGENTS.md entries SHOULD reference other files where appropriate." "Working Folder Correct": "AGENTS.md MUST be in the correct working folder for the job." + "Prevention Opportunities Evaluated": "The agent MUST evaluate whether deepschemas or deepreview rules could prevent issues encountered in the session. If opportunities exist, they SHOULD be created. If none are found, the agent MUST state why." shared_jobs: summary: "Make DeepWork library jobs available by configuring DEEPWORK_ADDITIONAL_JOBS_FOLDERS" diff --git a/src/deepwork/standard_schemas/claude_settings/claude_settings.schema.json b/src/deepwork/standard_schemas/claude_settings/claude_settings.schema.json new file mode 100644 index 00000000..c8a3096b --- /dev/null +++ b/src/deepwork/standard_schemas/claude_settings/claude_settings.schema.json @@ -0,0 +1,1802 @@ +{ + "_source": "Vendored from https://json.schemastore.org/claude-code-settings.json (SchemaStore community project). To update: fetch the latest version from that URL and replace this file. Last synced: 2026-04-01.", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/claude-code-settings.json", + "$defs": { + "permissionRule": { + "type": "string", + "description": "Tool permission rule.\nSee https://code.claude.com/docs/en/settings#permission-rule-syntax\nSee https://code.claude.com/docs/en/settings#tools-available-to-claude for full list of tools available to Claude.", + "pattern": "^((Agent|Bash|Edit|ExitPlanMode|Glob|Grep|KillShell|LSP|NotebookEdit|Read|Skill|TaskCreate|TaskGet|TaskList|TaskOutput|TaskStop|TaskUpdate|TodoWrite|ToolSearch|WebFetch|WebSearch|Write)(\\((?=.*[^)*?])[^)]+\\))?|mcp__.*)$", + "examples": [ + "Bash", + "Bash(npm run build)", + "Bash(git commit:*)", + "Bash(npm run:*)", + "Bash(ls*)", + "Bash(git * main)", + "Edit", + "Edit(/src/**/*.ts)", + "Read(./.env)", + "Read(./secrets/**)", + "Read(//Users/alice/secrets/**)", + "Read(~/Documents/*.pdf)", + "Agent(Explore)", + "WebFetch", + "WebFetch(domain:example.com)", + "mcp__puppeteer", + "mcp__github__search_repositories", + "mcp__github__*" + ] + }, + "hookCommand": { + "anyOf": [ + { + "type": "object", + "description": "Bash command hook", + "additionalProperties": false, + "required": [ + "type", + "command" + ], + "properties": { + "type": { + "type": "string", + "description": "Hook type", + "const": "command" + }, + "command": { + "type": "string", + "description": "Shell command to execute", + "minLength": 1 + }, + "timeout": { + "type": "number", + "description": "Optional timeout in seconds", + "exclusiveMinimum": 0 + }, + "async": { + "type": "boolean", + "description": "Run this hook asynchronously without blocking Claude Code" + }, + "statusMessage": { + "type": "string", + "description": "Custom spinner message displayed while the hook runs" + } + } + }, + { + "type": "object", + "description": "LLM prompt hook. See https://code.claude.com/docs/en/hooks#prompt-based-hooks", + "additionalProperties": false, + "required": [ + "type", + "prompt" + ], + "properties": { + "type": { + "type": "string", + "description": "Hook type", + "const": "prompt" + }, + "prompt": { + "type": "string", + "description": "Prompt to evaluate with LLM. Use $ARGUMENTS placeholder for hook input JSON.", + "minLength": 1 + }, + "model": { + "type": "string", + "description": "Model to use for evaluation. Defaults to a fast model" + }, + "timeout": { + "type": "number", + "description": "Optional timeout in seconds (default: 30)", + "exclusiveMinimum": 0 + }, + "statusMessage": { + "type": "string", + "description": "Custom spinner message displayed while the hook runs" + } + } + }, + { + "type": "object", + "description": "Agent hook with multi-turn tool access for verification. See https://code.claude.com/docs/en/hooks#agent-based-hooks", + "additionalProperties": false, + "required": [ + "type", + "prompt" + ], + "properties": { + "type": { + "type": "string", + "description": "Hook type", + "const": "agent" + }, + "prompt": { + "type": "string", + "description": "Prompt describing what to verify. Use $ARGUMENTS placeholder for hook input JSON.", + "minLength": 1 + }, + "model": { + "type": "string", + "description": "Model to use for evaluation. Defaults to a fast model" + }, + "timeout": { + "type": "number", + "description": "Optional timeout in seconds (default: 60)", + "exclusiveMinimum": 0 + }, + "statusMessage": { + "type": "string", + "description": "Custom spinner message displayed while the hook runs" + } + } + }, + { + "type": "object", + "description": "HTTP webhook hook. POST JSON to a URL and receive JSON response. See https://code.claude.com/docs/en/hooks#http-hooks", + "additionalProperties": false, + "required": [ + "type", + "url" + ], + "properties": { + "type": { + "type": "string", + "description": "Hook type", + "const": "http" + }, + "url": { + "type": "string", + "description": "URL to POST hook input JSON to. Endpoint must accept POST requests and return JSON.", + "minLength": 1 + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Custom HTTP headers (e.g., Authorization: Bearer token). Values support $VAR_NAME or ${VAR_NAME} interpolation." + }, + "allowedEnvVars": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "List of environment variable names permitted for interpolation in headers. If not set, no env var interpolation is allowed." + }, + "timeout": { + "type": "number", + "description": "Optional timeout in seconds (default: 30)", + "exclusiveMinimum": 0 + }, + "statusMessage": { + "type": "string", + "description": "Custom spinner message displayed while the hook runs" + } + } + } + ] + }, + "hookMatcher": { + "type": "object", + "description": "Hook matcher configuration with multiple hooks", + "additionalProperties": false, + "required": [ + "hooks" + ], + "properties": { + "matcher": { + "type": "string", + "description": "Optional pattern to match event contexts, case-sensitive. Behavior depends on event type. See https://code.claude.com/docs/en/hooks#matcher-patterns for event-specific details and examples" + }, + "hooks": { + "type": "array", + "description": "Array of hooks to execute", + "items": { + "$ref": "#/$defs/hookCommand" + } + } + } + } + }, + "description": "Configuration settings for Claude Code. Learn more: https://code.claude.com/docs/en/settings", + "allowTrailingCommas": true, + "additionalProperties": true, + "type": "object", + "properties": { + "$schema": { + "type": "string", + "description": "JSON Schema reference for Claude Code settings" + }, + "apiKeyHelper": { + "type": "string", + "description": "Path to a script that outputs authentication values", + "examples": [ + "/bin/generate_temp_api_key.sh" + ], + "minLength": 1 + }, + "autoMemoryEnabled": { + "type": "boolean", + "description": "Enable automatic memory saves that capture useful context to .claude/memory/. Also configurable via CLAUDE_CODE_DISABLE_AUTO_MEMORY environment variable (set to 1 to disable, 0 to enable). See https://code.claude.com/docs/en/memory#auto-memory", + "default": true + }, + "autoUpdatesChannel": { + "type": "string", + "enum": [ + "stable", + "latest" + ], + "description": "Release channel to follow for updates. Use \"stable\" for a version that is typically about one week old and skips versions with major regressions, or \"latest\" (default) for the most recent release. Set DISABLE_AUTOUPDATER=1 to disable updates entirely.", + "default": "latest" + }, + "awsCredentialExport": { + "type": "string", + "description": "Path to a script that exports AWS credentials", + "examples": [ + "/bin/generate_aws_grant.sh" + ], + "minLength": 1 + }, + "awsAuthRefresh": { + "type": "string", + "description": "Path to a script that refreshes AWS authentication", + "examples": [ + "aws sso login --profile myprofile" + ], + "minLength": 1 + }, + "claudeMdExcludes": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Glob patterns for CLAUDE.md files to exclude from loading. Useful in monorepos to skip irrelevant instructions from other teams. Patterns match against absolute file paths. Arrays merge across settings layers. Managed policy CLAUDE.md files cannot be excluded. See https://code.claude.com/docs/en/memory#exclude-specific-claudemd-files", + "examples": [ + [ + "**/monorepo/CLAUDE.md", + "/home/user/monorepo/other-team/.claude/rules/**" + ] + ] + }, + "cleanupPeriodDays": { + "type": "integer", + "minimum": 0, + "description": "Number of days to retain chat transcripts (0 to disable cleanup)", + "examples": [ + 20, + 30, + 60 + ], + "default": 30 + }, + "env": { + "type": "object", + "additionalProperties": false, + "description": "Environment variables to set for Claude Code sessions. Many environment variables provide settings dimensions not available as dedicated settings.json properties (e.g., thinking tokens, prompt caching, bash timeouts, shell configuration). See https://code.claude.com/docs/en/settings#environment-variables for the full list.\nUNDOCUMENTED: CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS (plugin marketplace git timeout in ms, default 120000, see https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#2151).\nUNDOCUMENTED: ENABLE_CLAUDEAI_MCP_SERVERS (set to false to opt out of claude.ai MCP servers, see https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#2163).", + "examples": [ + { + "ANTHROPIC_MODEL": "claude-opus-4-1", + "ANTHROPIC_SMALL_FAST_MODEL": "claude-3-5-haiku-latest" + } + ], + "default": {}, + "patternProperties": { + "^[A-Z_][A-Z0-9_]*$": { + "type": "string", + "description": "Environment variable value" + } + } + }, + "attribution": { + "type": "object", + "description": "Customize attribution for git commits and pull requests. See https://code.claude.com/docs/en/settings#attribution-settings", + "additionalProperties": false, + "properties": { + "commit": { + "type": "string", + "description": "Attribution for git commits, including any trailers. Empty string hides commit attribution" + }, + "pr": { + "type": "string", + "description": "Attribution for pull request descriptions. Empty string hides pull request attribution" + } + } + }, + "includeGitInstructions": { + "type": "boolean", + "description": "Include built-in git commit and PR workflow instructions in Claude's system prompt. Also configurable via CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS environment variable (set to 1 to disable). See https://code.claude.com/docs/en/settings#available-settings", + "default": true + }, + "includeCoAuthoredBy": { + "type": "boolean", + "description": "DEPRECATED. Use 'attribution' instead. Whether to include the co-authored-by Claude byline in git commits and pull requests (default: true)", + "default": true + }, + "plansDirectory": { + "type": "string", + "description": "Customize where plan files are stored. Path is relative to project root (default: ~/.claude/plans)", + "default": "~/.claude/plans", + "examples": [ + "./plans" + ] + }, + "respectGitignore": { + "type": "boolean", + "description": "Control whether the @ file picker respects .gitignore patterns. When true (default), files matching .gitignore patterns are excluded from suggestions", + "default": true + }, + "permissions": { + "type": "object", + "properties": { + "allow": { + "type": "array", + "items": { + "$ref": "#/$defs/permissionRule" + }, + "description": "List of permission rules for allowed operations", + "uniqueItems": true + }, + "deny": { + "type": "array", + "items": { + "$ref": "#/$defs/permissionRule" + }, + "description": "List of permission rules for denied operations", + "uniqueItems": true + }, + "ask": { + "type": "array", + "items": { + "$ref": "#/$defs/permissionRule" + }, + "description": "List of permission rules that should always prompt for confirmation", + "uniqueItems": true + }, + "defaultMode": { + "type": "string", + "enum": [ + "acceptEdits", + "bypassPermissions", + "default", + "delegate", + "dontAsk", + "plan" + ], + "description": "Default permission mode.\n\"default\": prompts on first use.\n\"acceptEdits\": auto-accepts file edits.\n\"plan\": read-only, no modifications.\nUNDOCUMENTED. \"delegate\": coordination-only for agent team leads (agent teams are experimental; enable via CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS).\n\"dontAsk\": auto-denies unless pre-approved via permissions.\n\"bypassPermissions\": skips all prompts (use only in isolated environments).\nSee https://code.claude.com/docs/en/permissions" + }, + "disableBypassPermissionsMode": { + "type": "string", + "enum": [ + "disable" + ], + "description": "Disable the ability to bypass permission prompts" + }, + "additionalDirectories": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Additional directories to include in the permission scope", + "examples": [ + [ + "//Users/alice/Documents", + "~/projects" + ] + ], + "uniqueItems": true + } + }, + "additionalProperties": false, + "description": "Tool usage permissions configuration.\nSee https://code.claude.com/docs/en/permissions and https://code.claude.com/docs/en/settings#permission-settings\nSee https://code.claude.com/docs/en/settings#tools-available-to-claude for full list of tools available to Claude.", + "examples": [ + { + "allow": [ + "Bash(git add:*)" + ], + "ask": [ + "Bash(gh pr create:*)", + "Bash(git commit:*)" + ], + "deny": [ + "Read(*.env)", + "Bash(rm:*)", + "Bash(curl:*)" + ], + "defaultMode": "default" + } + ] + }, + "language": { + "type": "string", + "description": "Preferred language for Claude's responses", + "examples": [ + "japanese", + "spanish", + "french" + ] + }, + "model": { + "type": "string", + "description": "Override the default model used by Claude Code. For finer control, use environment variables: ANTHROPIC_MODEL (runtime override), ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL (per-class pinning), CLAUDE_CODE_SUBAGENT_MODEL (subagent model). See https://code.claude.com/docs/en/model-config" + }, + "availableModels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Restrict which models users can select. When defined at multiple settings levels (user, project, etc.), arrays are merged and deduplicated. See https://code.claude.com/docs/en/model-config#restrict-model-selection", + "examples": [ + [ + "sonnet", + "haiku" + ] + ] + }, + "modelOverrides": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Map Anthropic model IDs to provider-specific model IDs such as Bedrock inference profile ARNs, Vertex AI version names, or Foundry deployment names. Each model picker entry uses its mapped value when calling the provider API. Unknown keys are ignored. See https://code.claude.com/docs/en/model-config#override-model-ids-per-version", + "examples": [ + { + "claude-opus-4-6": "arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/opus-prod" + } + ] + }, + "effortLevel": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "description": "Control Opus 4.6 adaptive reasoning effort. Lower effort is faster and cheaper for straightforward tasks, higher effort provides deeper reasoning. Defaults vary by model and plan (Opus 4.6 defaults to medium for Max and Team subscribers). Use /effort auto to reset to model default. Also configurable via CLAUDE_CODE_EFFORT_LEVEL environment variable. See https://code.claude.com/docs/en/model-config#adjust-effort-level" + }, + "fastMode": { + "type": "boolean", + "description": "Enable fast mode for Opus 4.6 (research preview). Fast mode uses the same model with 2.5x faster output at higher per-token cost. Requires extra usage enabled. Alternatively, toggle with /fast command. See https://code.claude.com/docs/en/fast-mode", + "default": false + }, + "fastModePerSessionOptIn": { + "type": "boolean", + "description": "Require per-session opt-in for fast mode. When true, fast mode does not persist across sessions and users must enable it with /fast each session. Useful for controlling costs. See https://code.claude.com/docs/en/fast-mode", + "default": false + }, + "feedbackSurveyRate": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Probability (0\u20131) that the session quality survey appears when eligible. A value of 0.05 means 5% of eligible sessions. See https://code.claude.com/docs/en/settings", + "examples": [ + 0.05 + ] + }, + "enableAllProjectMcpServers": { + "type": "boolean", + "description": "Whether to automatically approve all MCP servers in the project. See https://code.claude.com/docs/en/mcp", + "examples": [ + true + ] + }, + "enabledMcpjsonServers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "List of approved MCP servers from .mcp.json. See https://code.claude.com/docs/en/mcp", + "examples": [ + [ + "memory", + "github" + ] + ] + }, + "disabledMcpjsonServers": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "List of rejected MCP servers from .mcp.json. See https://code.claude.com/docs/en/mcp", + "examples": [ + [ + "filesystem" + ] + ] + }, + "allowedMcpServers": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Name of the MCP server that users are allowed to configure" + } + }, + "required": [ + "serverName" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "serverCommand": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Exact command and arguments used to start stdio servers" + } + }, + "required": [ + "serverCommand" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "serverUrl": { + "type": "string", + "description": "URL pattern for remote servers, supports wildcards (e.g., https://*.example.com/*)" + } + }, + "required": [ + "serverUrl" + ], + "additionalProperties": false + } + ] + }, + "description": "Enterprise allowlist of MCP servers that can be used. Applies to all scopes including enterprise servers from managed-mcp.json. If undefined, all servers are allowed. If empty array, no servers are allowed. Denylist takes precedence - if a server is on both lists, it is denied. See https://code.claude.com/docs/en/mcp#restriction-options" + }, + "deniedMcpServers": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Name of the MCP server that is explicitly blocked" + } + }, + "required": [ + "serverName" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "serverCommand": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Exact command and arguments used to start stdio servers" + } + }, + "required": [ + "serverCommand" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "serverUrl": { + "type": "string", + "description": "URL pattern for remote servers, supports wildcards (e.g., https://*.example.com/*)" + } + }, + "required": [ + "serverUrl" + ], + "additionalProperties": false + } + ] + }, + "description": "Enterprise denylist of MCP servers that are explicitly blocked. If a server is on the denylist, it will be blocked across all scopes including enterprise. Denylist takes precedence over allowlist - if a server is on both lists, it is denied. See https://code.claude.com/docs/en/mcp#restriction-options" + }, + "httpHookAllowedEnvVars": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Allowlist of environment variable names HTTP hooks may interpolate into headers. When set, each hook's effective allowedEnvVars is the intersection with this list. Undefined = no restriction. Arrays merge across settings sources. See https://code.claude.com/docs/en/settings#hook-configuration", + "examples": [ + [ + "MY_TOKEN", + "HOOK_SECRET" + ] + ] + }, + "hooks": { + "type": "object", + "additionalProperties": false, + "description": "Custom commands to run before/after tool executions. See https://code.claude.com/docs/en/hooks", + "examples": [ + { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "prettier --write", + "timeout": 5 + } + ] + } + ] + } + ], + "properties": { + "PreToolUse": { + "type": "array", + "description": "Hooks that run before tool calls", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "PostToolUse": { + "type": "array", + "description": "Hooks that run after tool completion", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "PostToolUseFailure": { + "type": "array", + "description": "Hooks that run after a tool fails", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "PermissionRequest": { + "type": "array", + "description": "Hooks that run when a permission dialog appears", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "Notification": { + "type": "array", + "description": "Hooks that trigger on notifications", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "UserPromptSubmit": { + "type": "array", + "description": "Hooks that run when a user submits a prompt", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "Stop": { + "type": "array", + "description": "Hooks that run when agents finish responding. Does not run on user interrupt", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "SubagentStart": { + "type": "array", + "description": "Hooks that run when a subagent is spawned", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "SubagentStop": { + "type": "array", + "description": "Hooks that run when subagents finish responding", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "PreCompact": { + "type": "array", + "description": "Hooks that run before the context is compacted", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "PostCompact": { + "type": "array", + "description": "Hooks that run after the context is compacted. See https://code.claude.com/docs/en/hooks", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "Elicitation": { + "type": "array", + "description": "Hooks that run when an MCP server requests user input during a tool call. See https://code.claude.com/docs/en/hooks", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "ElicitationResult": { + "type": "array", + "description": "Hooks that run after a user responds to an MCP elicitation, before the response is sent back to the server. See https://code.claude.com/docs/en/hooks", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "TeammateIdle": { + "type": "array", + "description": "Hooks that run when an agent team teammate is about to go idle. Exit code 2 sends feedback and keeps the teammate working. Does not support matchers. Agent teams are experimental. See https://code.claude.com/docs/en/hooks#teammateidle", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "TaskCompleted": { + "type": "array", + "description": "Hooks that run when a task is being marked as completed. Exit code 2 prevents completion and sends feedback. Does not support matchers. See https://code.claude.com/docs/en/hooks#taskcompleted", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "Setup": { + "type": "array", + "description": "UNDOCUMENTED. Hooks that run during repository initialization (--init, --init-only) or maintenance (--maintenance)", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "InstructionsLoaded": { + "type": "array", + "description": "Hooks that run when a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded (e.g., nested traversal, path glob match). No decision control; used for audit logging and observability. Does not support matchers. See https://code.claude.com/docs/en/hooks#instructionsloaded", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "ConfigChange": { + "type": "array", + "description": "Hooks that run when settings, managed settings, or skill files change during a session. Supports matchers: user_settings, project_settings, local_settings, policy_settings, skills. Command handlers only. Exit code 2 blocks the change (except policy_settings which is audit-only). See https://code.claude.com/docs/en/hooks#configchange", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "WorktreeCreate": { + "type": "array", + "description": "Hooks that run when a worktree is created via --worktree or isolation: \"worktree\" in subagents. Command handlers only, no matchers. Hook must print absolute path to created worktree on stdout; non-zero exit fails creation. See https://code.claude.com/docs/en/hooks#worktreecreate", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "WorktreeRemove": { + "type": "array", + "description": "Hooks that run when a worktree is being removed at session exit or when a subagent finishes. Command handlers only, no matchers. Used for cleanup tasks; cannot block removal. See https://code.claude.com/docs/en/hooks#worktreeremove", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "SessionStart": { + "type": "array", + "description": "Hooks that run when a new session starts", + "items": { + "$ref": "#/$defs/hookMatcher" + } + }, + "SessionEnd": { + "type": "array", + "description": "Hooks that run when a session ends", + "items": { + "$ref": "#/$defs/hookMatcher" + } + } + } + }, + "disableAllHooks": { + "type": "boolean", + "description": "Disable all hooks and statusLine execution. When true in managed settings, user and project-level disableAllHooks cannot override it. See https://code.claude.com/docs/en/hooks#disable-or-remove-hooks" + }, + "allowedHttpHookUrls": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Allowlist of URL patterns that HTTP hooks may target. Supports * as a wildcard. When set, hooks with non-matching URLs are blocked. Undefined = no restriction, empty array = block all HTTP hooks. Arrays merge across settings sources. See https://code.claude.com/docs/en/settings#hook-configuration", + "examples": [ + [ + "https://hooks.example.com/*", + "http://localhost:*" + ] + ] + }, + "allowManagedHooksOnly": { + "type": "boolean", + "description": "(Managed settings only) Prevent loading of user, project, and plugin hooks. Only allows managed hooks and SDK hooks. See https://code.claude.com/docs/en/settings#hook-configuration" + }, + "allowManagedPermissionRulesOnly": { + "type": "boolean", + "description": "(Managed settings only) Prevent user and project settings from defining allow, ask, or deny permission rules. Only rules in managed settings apply. See https://code.claude.com/docs/en/settings#permission-settings" + }, + "statusLine": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of status line handler; must be set to \"command\" to run a custom shell script that receives JSON session data via stdin.", + "const": "command" + }, + "command": { + "type": "string", + "description": "A shell command or path to a script that displays session information (context usage, costs, git status, etc.) by reading JSON data from stdin and writing output to stdout. See https://code.claude.com/docs/en/statusline" + }, + "padding": { + "type": "number", + "description": "Optional number of extra horizontal spacing characters added to the status line content; defaults to 0." + } + }, + "required": [ + "type", + "command" + ], + "additionalProperties": false, + "description": "Custom status line display configuration. See https://code.claude.com/docs/en/statusline", + "examples": [ + { + "type": "command", + "command": "~/.claude/statusline.sh" + } + ] + }, + "fileSuggestion": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of file suggestion handler; must be set to \"command\" to execute a custom shell script that generates file suggestions for the @ file picker.", + "const": "command" + }, + "command": { + "type": "string", + "description": "Shell command to execute for file suggestions" + } + }, + "required": [ + "type", + "command" + ], + "additionalProperties": false, + "description": "Configure a custom script for @ file autocomplete. See https://code.claude.com/docs/en/settings#file-suggestion-settings", + "examples": [ + { + "type": "command", + "command": "~/.claude/file-suggestion.sh" + } + ] + }, + "enabledPlugins": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + }, + { + "not": {} + } + ] + }, + "description": "Enabled plugins using plugin-id@marketplace-id format. Example: { \"formatter@anthropic-tools\": true }. See https://code.claude.com/docs/en/plugins" + }, + "extraKnownMarketplaces": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "source": { + "anyOf": [ + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "url" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Direct URL to marketplace.json file" + } + }, + "required": [ + "source", + "url" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "hostPattern" + }, + "hostPattern": { + "type": "string", + "description": "Git host pattern to trust for repositories in source specifications" + } + }, + "required": [ + "source", + "hostPattern" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "github" + }, + "repo": { + "type": "string", + "description": "GitHub repository in owner/repo format" + }, + "ref": { + "type": "string", + "description": "Git branch or tag to use (e.g., \"main\", \"v1.0.0\"). Defaults to repository default branch." + }, + "path": { + "type": "string", + "description": "Path to marketplace.json within repo (defaults to .claude-plugin/marketplace.json)" + } + }, + "required": [ + "source", + "repo" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "git" + }, + "url": { + "type": "string", + "pattern": "\\.git$", + "description": "Full git repository URL" + }, + "ref": { + "type": "string", + "description": "Git branch or tag to use (e.g., \"main\", \"v1.0.0\"). Defaults to repository default branch." + }, + "path": { + "type": "string", + "description": "Path to marketplace.json within repo (defaults to .claude-plugin/marketplace.json)" + } + }, + "required": [ + "source", + "url" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "npm" + }, + "package": { + "type": "string", + "description": "NPM package containing marketplace.json" + } + }, + "required": [ + "source", + "package" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "file" + }, + "path": { + "type": "string", + "description": "Local file path to marketplace.json" + } + }, + "required": [ + "source", + "path" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "directory" + }, + "path": { + "type": "string", + "description": "Local directory containing .claude-plugin/marketplace.json" + } + }, + "required": [ + "source", + "path" + ], + "additionalProperties": false + } + ], + "description": "Where to fetch the marketplace from" + }, + "installLocation": { + "type": "string", + "description": "Local cache path where marketplace manifest is stored (auto-generated if not provided)" + } + }, + "required": [ + "source" + ], + "additionalProperties": false + }, + "description": "Additional marketplaces to make available for this repository. Typically used in repository .claude/settings.json to ensure team members have required plugin sources. See https://code.claude.com/docs/en/plugin-marketplaces" + }, + "strictKnownMarketplaces": { + "type": "array", + "description": "(Managed settings only) Allowlist of plugin marketplaces users can add. Undefined = no restrictions, empty array = lockdown. Uses exact matching for source specifications. See https://code.claude.com/docs/en/settings#strictknownmarketplaces", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "hostPattern" + }, + "hostPattern": { + "type": "string", + "description": "Git host pattern to trust for repositories in source specifications" + } + }, + "required": [ + "source", + "hostPattern" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "github" + }, + "repo": { + "type": "string", + "description": "GitHub repository in owner/repo format" + }, + "ref": { + "type": "string", + "description": "Git branch, tag, or SHA" + }, + "path": { + "type": "string", + "description": "Subdirectory path" + } + }, + "required": [ + "source", + "repo" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "git" + }, + "url": { + "type": "string", + "description": "Full git repository URL" + }, + "ref": { + "type": "string", + "description": "Git branch, tag, or SHA" + }, + "path": { + "type": "string", + "description": "Subdirectory path" + } + }, + "required": [ + "source", + "url" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "url" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Direct URL to marketplace.json" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP headers for authenticated access" + } + }, + "required": [ + "source", + "url" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "npm" + }, + "package": { + "type": "string", + "description": "NPM package name (supports scoped packages)" + } + }, + "required": [ + "source", + "package" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "file" + }, + "path": { + "type": "string", + "description": "Absolute path to marketplace.json file" + } + }, + "required": [ + "source", + "path" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "directory" + }, + "path": { + "type": "string", + "description": "Absolute path to directory containing .claude-plugin/marketplace.json" + } + }, + "required": [ + "source", + "path" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Identifies the marketplace source type", + "const": "pathPattern" + }, + "pathPattern": { + "type": "string", + "description": "Regex pattern to match file or directory paths for marketplace sources" + } + }, + "required": [ + "source", + "pathPattern" + ], + "additionalProperties": false + } + ] + }, + "examples": [ + [ + { + "source": "github", + "repo": "acme-corp/approved-plugins" + }, + { + "source": "npm", + "package": "@acme-corp/compliance-plugins" + } + ] + ] + }, + "skippedMarketplaces": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "List of marketplace names the user has chosen not to install when prompted" + }, + "skippedPlugins": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "List of plugin IDs (plugin@marketplace format) the user has chosen not to install when prompted" + }, + "forceLoginMethod": { + "type": "string", + "enum": [ + "claudeai", + "console" + ], + "description": "Force a specific login method: \"claudeai\" for Claude Pro/Max, \"console\" for Console billing", + "examples": [ + "claudeai" + ] + }, + "forceLoginOrgUUID": { + "type": "string", + "description": "Organization UUID to use for OAuth login", + "examples": [ + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + ], + "minLength": 1 + }, + "otelHeadersHelper": { + "type": "string", + "description": "Path to a script that outputs OpenTelemetry headers", + "minLength": 1 + }, + "outputStyle": { + "type": "string", + "description": "Controls the output style for assistant responses. Built-in styles: default, Explanatory, Learning. Custom styles can be added in ~/.claude/output-styles/ or .claude/output-styles/. See https://code.claude.com/docs/en/output-styles", + "examples": [ + "default", + "Explanatory", + "Learning" + ], + "minLength": 1 + }, + "skipWebFetchPreflight": { + "type": "boolean", + "description": "Skip the WebFetch blocklist check for enterprise environments with restrictive security policies" + }, + "sandbox": { + "type": "object", + "description": "Sandbox execution configuration. See https://code.claude.com/docs/en/sandboxing", + "properties": { + "network": { + "type": "object", + "description": "Configures network isolation settings for the sandboxed bash environment, including domain restrictions, Unix socket access, and custom proxy configuration.", + "properties": { + "allowUnixSockets": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allow Unix domain sockets for local IPC (SSH agent, Docker, etc.). Provide an array of specific paths. Defaults to blocking if not specified" + }, + "allowLocalBinding": { + "type": "boolean", + "description": "Allow binding to local network addresses (e.g., localhost ports). Defaults to false if not specified" + }, + "httpProxyPort": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "description": "HTTP proxy port to use for network filtering. If not specified, a proxy server will be started automatically" + }, + "socksProxyPort": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "description": "SOCKS proxy port to use for network filtering. If not specified, a proxy server will be started automatically" + }, + "allowAllUnixSockets": { + "type": "boolean", + "description": "Allow all Unix domain socket connections. If true, this overrides allowUnixSockets" + }, + "allowedDomains": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Allowlist of network domains for sandboxed commands. Supports wildcard patterns like *.example.com" + }, + "allowManagedDomainsOnly": { + "type": "boolean", + "description": "(Managed settings only) Only allowedDomains and WebFetch(domain:...) allow rules from managed settings are respected. User, project, local, and flag settings domains are ignored. Denied domains are still respected from all sources. Non-allowed domains are automatically blocked without user prompts." + } + }, + "additionalProperties": false + }, + "ignoreViolations": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of filesystem paths to ignore sandbox violations for when this command pattern matches" + }, + "description": "Map of command patterns to filesystem paths to ignore violations for. Use \"*\" to match all commands" + }, + "excludedCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Commands that should never run in the sandbox (e.g., [\"git\", \"docker\"])" + }, + "autoAllowBashIfSandboxed": { + "type": "boolean", + "description": "Automatically allow bash commands without prompting when they run in the sandbox. Only applies to commands that will run sandboxed.", + "default": true + }, + "enableWeakerNetworkIsolation": { + "type": "boolean", + "description": "macOS only. Allow access to the system TLS trust service (com.apple.trustd.agent) in the sandbox. Required for Go-based tools like gh, gcloud, and terraform to verify TLS certificates when using httpProxyPort with a MITM proxy and custom CA. Reduces security by opening a potential data exfiltration path. Default: false. See https://code.claude.com/docs/en/settings#sandbox-settings", + "default": false + }, + "enableWeakerNestedSandbox": { + "type": "boolean", + "description": "Enable weaker sandbox mode for unprivileged docker environments where --proc mounting fails. This significantly reduces the strength of the sandbox and should only be used when this risk is acceptable.Default: false (secure)." + }, + "allowUnsandboxedCommands": { + "type": "boolean", + "description": "Allow commands to run outside the sandbox via the dangerouslyDisableSandbox parameter. When false, the dangerouslyDisableSandbox parameter is completely ignored and all commands must run sandboxed. Default: true." + }, + "enabled": { + "type": "boolean", + "description": "Enable sandboxed bash" + }, + "filesystem": { + "type": "object", + "description": "Filesystem access control for sandboxed commands. See https://code.claude.com/docs/en/sandboxing#filesystem-isolation", + "properties": { + "allowWrite": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Paths where subprocesses are allowed to write. Supports prefixes: // (absolute), ~/ (home directory), / (relative to settings file), ./ or no prefix (relative path)" + }, + "denyWrite": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Paths where subprocesses are explicitly denied write access. Takes precedence over allowWrite" + }, + "denyRead": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Paths where subprocesses are explicitly denied read access" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "spinnerVerbs": { + "type": "object", + "description": "Customize the verbs shown in spinner progress messages", + "properties": { + "mode": { + "type": "string", + "enum": [ + "append", + "replace" + ], + "description": "How to combine custom verbs with default spinner verbs: 'append' adds custom verbs to the default list, 'replace' uses only custom verbs" + }, + "verbs": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1, + "description": "Custom verbs used in spinner progress text" + } + }, + "required": [ + "verbs" + ], + "additionalProperties": false + }, + "spinnerTipsEnabled": { + "type": "boolean", + "description": "Show tips in the spinner while Claude is working. Set to false to disable tips (default: true)", + "default": true + }, + "spinnerTipsOverride": { + "type": "object", + "description": "Customize the tips displayed in the spinner while Claude is working. See https://code.claude.com/docs/en/settings#available-settings", + "properties": { + "excludeDefault": { + "type": "boolean", + "description": "If true, only show custom tips. If false or absent, custom tips merge with built-in tips", + "default": false + }, + "tips": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Custom tip strings to display in the spinner", + "minItems": 1 + } + }, + "required": [ + "tips" + ], + "additionalProperties": false + }, + "terminalProgressBarEnabled": { + "type": "boolean", + "description": "Enable the terminal progress bar that shows progress in supported terminals like Windows Terminal and iTerm2 (default: true)", + "default": true + }, + "showTurnDuration": { + "type": "boolean", + "description": "Show turn duration messages after responses (e.g., \"Cooked for 1m 6s\"). Set to false to hide these messages (default: true)", + "default": true + }, + "prefersReducedMotion": { + "type": "boolean", + "description": "Reduce or disable UI animations (spinners, shimmer, flash effects) for accessibility", + "default": false + }, + "alwaysThinkingEnabled": { + "type": "boolean", + "description": "Enable extended thinking by default for all sessions. Typically configured via the /config command rather than editing directly. See https://code.claude.com/docs/en/common-workflows#use-extended-thinking-thinking-mode" + }, + "companyAnnouncements": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Company announcements to display at startup (one will be randomly selected if multiple are provided)" + }, + "teammateMode": { + "type": "string", + "enum": [ + "auto", + "in-process", + "tmux" + ], + "description": "How agent team teammates display: \"auto\" picks split panes in tmux or iTerm2, in-process otherwise. Agent teams are experimental and disabled by default. Enable them by adding CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS to your settings.json or environment. See https://code.claude.com/docs/en/agent-teams", + "default": "auto" + }, + "worktree": { + "type": "object", + "additionalProperties": false, + "description": "Configuration for --worktree sessions. See https://code.claude.com/docs/en/settings", + "properties": { + "sparsePaths": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "description": "Directories to check out in each worktree via git sparse-checkout (cone mode). Only the listed paths are written to disk, which is faster in large monorepos.", + "examples": [ + [ + "packages/my-app", + "shared/utils" + ] + ] + } + } + }, + "pluginTrustMessage": { + "type": "string", + "description": "(Managed settings only) Custom message appended to the plugin trust warning shown before installation. Use to provide organization-specific context about approved plugins. See https://code.claude.com/docs/en/settings#plugin-settings", + "minLength": 1 + }, + "pluginConfigs": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "mcpServers": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "description": "User configuration values for MCP servers keyed by server name" + } + }, + "additionalProperties": false + }, + "description": "Per-plugin configuration including MCP server user configs, keyed by plugin ID (plugin@marketplace format). See https://code.claude.com/docs/en/plugins" + }, + "allowManagedMcpServersOnly": { + "type": "boolean", + "description": "(Managed settings only) Only allowedMcpServers from managed settings are respected. deniedMcpServers still merges from all sources. Users can still add their own MCP servers, but only the admin-defined allowlist applies." + }, + "blockedMarketplaces": { + "type": "array", + "description": "(Managed settings only) Blocklist of marketplace sources. These exact sources are blocked from being added as marketplaces. The check happens before downloading, so blocked sources never touch the filesystem.", + "items": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "url", + "description": "Block marketplace fetched from direct URL" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Direct URL to marketplace.json file" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Custom HTTP headers (e.g., for authentication)" + } + }, + "required": [ + "source", + "url" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "github", + "description": "Block marketplace from GitHub repository" + }, + "repo": { + "type": "string", + "description": "GitHub repository in owner/repo format" + }, + "ref": { + "type": "string", + "description": "Git branch or tag to use" + }, + "path": { + "type": "string", + "description": "Path to marketplace.json within repo" + } + }, + "required": [ + "source", + "repo" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "git", + "description": "Block marketplace from git repository URL" + }, + "url": { + "type": "string", + "pattern": ".*\\.git$", + "description": "Full git repository URL" + }, + "ref": { + "type": "string", + "description": "Git branch or tag to use" + }, + "path": { + "type": "string", + "description": "Path to marketplace.json within repo" + } + }, + "required": [ + "source", + "url" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "npm", + "description": "Block marketplace from NPM package" + }, + "package": { + "type": "string", + "description": "NPM package containing marketplace.json" + } + }, + "required": [ + "source", + "package" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "file", + "description": "Block marketplace from local file" + }, + "path": { + "type": "string", + "description": "Local file path to marketplace.json" + } + }, + "required": [ + "source", + "path" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "directory", + "description": "Block marketplace from local directory" + }, + "path": { + "type": "string", + "description": "Local directory containing .claude-plugin/marketplace.json" + } + }, + "required": [ + "source", + "path" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "hostPattern", + "description": "Block marketplace by host pattern matching" + }, + "hostPattern": { + "type": "string", + "description": "Regex pattern to match the host/domain extracted from any marketplace source type" + } + }, + "required": [ + "source", + "hostPattern" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "type": "string", + "const": "pathPattern", + "description": "Block marketplace by file/directory path pattern matching" + }, + "pathPattern": { + "type": "string", + "description": "Regex pattern to match file or directory paths for marketplace sources" + } + }, + "required": [ + "source", + "pathPattern" + ] + } + ] + } + } + }, + "title": "Claude Code Settings" +} \ No newline at end of file diff --git a/src/deepwork/standard_schemas/claude_settings/deepschema.yml b/src/deepwork/standard_schemas/claude_settings/deepschema.yml new file mode 100644 index 00000000..b18d4d11 --- /dev/null +++ b/src/deepwork/standard_schemas/claude_settings/deepschema.yml @@ -0,0 +1,79 @@ +summary: "Claude Code settings.json and settings.local.json configuration files." + +instructions: | + Claude Code settings files control permissions, hooks, sandbox, model preferences, + and plugin configuration. There are three scopes with distinct purposes: + + - `.claude/settings.json` — shared project settings, committed to git. Team-wide + permissions, hooks, and MCP server approvals. + - `.claude/settings.local.json` — personal per-project overrides, gitignored. + Sensitive credentials, personal MCP configs, local permission tweaks. + - `~/.claude/settings.json` — user-global defaults for all projects. + + Precedence (highest to lowest): managed → CLI args → settings.local.json → + settings.json → ~/.claude/settings.json → defaults. Deny rules at ANY level + cannot be overridden by allow rules at any other level. + + Key sections: permissions (allow/ask/deny tool rules), hooks (lifecycle event + handlers), sandbox (OS-level isolation), env (environment variables), and + various global config fields (model, attribution, autoMode, etc.). + + Gotchas to keep in mind when editing these files: + + - Sandbox path settings use DIFFERENT prefix semantics than permission rules: + `/path` means absolute (not project-relative), `~/` means home, `./` or + bare path means project root. Do not confuse these with Read/Edit path rules. + - When `autoMode.allow` or `autoMode.soft_deny` arrays are set, they completely + replace the built-in defaults rather than merging. Review defaults via + `claude auto-mode defaults` before overriding. + - When `sandbox.enabled` is true, `sandbox.filesystem.allowWrite` should include + the project directory and any tool output directories. An overly restrictive + sandbox without necessary write paths will cause tool failures. + - The `attribution` object supersedes the deprecated `includeCoAuthoredBy` + boolean. Prefer `attribution` for new configurations. + +matchers: + - ".claude/settings.json" + - ".claude/settings.local.json" + - "**/.claude/settings.json" + - "**/.claude/settings.local.json" + +json_schema_path: "claude_settings.schema.json" + +requirements: + bash-wildcard-word-boundaries: > + Bash permission patterns MUST use the `:*` style to enforce word + boundaries (e.g., `Bash(npm run:*)`) and only use regular * if it is + clearly a situation where the whitespace is not appropriate. Patterns + MUST be reviewed for unintended broad matching. + + read-edit-path-prefixes: > + Read and Edit permission specifiers MUST use the correct path prefix + convention: `./**` or bare paths for project-relative, `~` for home + directory, `//` (double slash) for absolute filesystem paths. A single + leading `/` is project-relative, NOT absolute — this is a common mistake. + + no-secrets-in-shared-settings: > + `.claude/settings.json` (the shared, committed file) MUST NOT contain + secrets, API keys, tokens, or credentials. Sensitive values MUST go in + `.claude/settings.local.json` (gitignored) or environment variables. + + automode-not-in-shared-settings: > + `autoMode` configuration MUST NOT appear in `.claude/settings.json` (shared + project settings). Claude Code ignores autoMode from shared project settings + to prevent malicious repos from weakening security. Use user-level or + managed settings instead. + + mcp-tools-pattern-valid: > + MCP tool permission patterns MUST follow the format + `mcp____` or use a wildcard `mcp____*` + for all tools from a server. The double-underscore separators are required. + MCP tool permissions MUST NOT use `:*`. + +references: + - path: "https://json.schemastore.org/claude-code-settings.json" + description: "Community-maintained JSON Schema on SchemaStore — the most comprehensive structural schema available. Consider syncing our local schema with this periodically." + - path: "https://docs.anthropic.com/en/docs/claude-code/settings" + description: "Official Claude Code settings documentation — complete field reference and precedence rules." + - path: "https://docs.anthropic.com/en/docs/claude-code/security" + description: "Claude Code security model — permissions, sandboxing, and managed settings." diff --git a/src/deepwork/standard_schemas/deepschema/.deepschema.deepschema.yml.yml b/src/deepwork/standard_schemas/deepschema/.deepschema.deepschema.yml.yml new file mode 100644 index 00000000..018ea0cb --- /dev/null +++ b/src/deepwork/standard_schemas/deepschema/.deepschema.deepschema.yml.yml @@ -0,0 +1,12 @@ +# This file is confusing in name because it is such a special case: +# This is the Anonymous DeepsShema for the DeepSchema file format itself. +requirements: + requirements-filesystem-verifiable-present: > + The deepschema standard schema MUST include a requirement enforcing that + DeepSchema requirements are verifiable by examining files on the + filesystem. (DW-REQ-011.10.1) + + no-structural-requirements-present: > + The deepschema standard schema MUST include a requirement enforcing that + DeepSchema requirements do not restate constraints already enforced by + json_schema_path or verification_bash_command. (DW-REQ-011.10.3) diff --git a/src/deepwork/standard_schemas/deepschema/deepschema.yml b/src/deepwork/standard_schemas/deepschema/deepschema.yml index 8e36b8bb..9efb3ab8 100644 --- a/src/deepwork/standard_schemas/deepschema/deepschema.yml +++ b/src/deepwork/standard_schemas/deepschema/deepschema.yml @@ -6,6 +6,7 @@ instructions: | their target file as .deepschema..yml. All keys are optional but a schema without requirements or matchers has no enforcement effect. + matchers: - ".deepwork/schemas/*/deepschema.yml" - "**/.deepschema.*.yml" @@ -14,6 +15,7 @@ json_schema_path: "deepschema_schema.json" requirements: requirements-use-rfc-2119: "Requirements MUST use RFC 2119 keywords (MUST, SHOULD, MAY, etc.) to indicate enforcement level." + requirements-filesystem-verifiable: "Each requirement MUST be verifiable by examining files on the filesystem. Requirements about processes, user behavior, or context not present in files SHOULD be in the instructions section instead." matchers-for-named: "Named DeepSchemas SHOULD include matchers so they can match files for review generation." summary-for-named: "Named DeepSchemas SHOULD include a summary for discoverability." valid-parent-refs: "Any schema listed in parent_deep_schemas MUST reference an existing named schema." @@ -21,3 +23,4 @@ requirements: json-schema-path-valid: "If json_schema_path is set, it MUST point to a valid JSON Schema file relative to the schema directory." examples-have-descriptions: "Each entry in examples SHOULD have both a path and a description." references-have-descriptions: "Each entry in references SHOULD have both a path and a description." + no-structural-requirements: "Requirements MUST NOT restate constraints that are already enforced by the schema's json_schema_path or verification_bash_command. This includes syntactic validity (e.g., 'must be valid JSON'), field types (e.g., 'must be an array of strings'), allowed enum values, required fields, and structural shape. These SHOULD be expressed in the JSON Schema file. Requirements SHOULD focus on semantic rules, behavioral gotchas, and cross-field concerns that JSON Schema cannot express." diff --git a/src/deepwork/standard_schemas/requirements_file/deepschema.yml b/src/deepwork/standard_schemas/requirements_file/deepschema.yml new file mode 100644 index 00000000..4afd0809 --- /dev/null +++ b/src/deepwork/standard_schemas/requirements_file/deepschema.yml @@ -0,0 +1,78 @@ +summary: "RFC 2119 requirements specification files." + +instructions: | + Requirements files define formal specifications using RFC 2119 keywords. + They follow the naming convention `REQ-NNN-.md` where NNN is a + zero-padded number. + + Each file has: + - A `# REQ-NNN: Title` top-level heading matching the filename prefix. + - An `## Overview` section describing the subsystem. + - A `## Requirements` section containing one or more `### REQ-NNN.M: Title` + subsections, each with a numbered list of requirements. + + Every requirement statement must be specific enough to be verifiable by either + an automated test or a DeepReview rule. Vague requirements like "SHOULD be fast" + or "MUST be user-friendly" are not acceptable — they must include a concrete, + evaluable criterion. + + When adding new requirements: + - Assign the next sequential section number (REQ-NNN.M where M is next unused). + - Number requirements sequentially within each section (1, 2, 3...). + - Each requirement gets exactly one RFC 2119 keyword (MUST, SHOULD, MAY, etc.). + + Requirement ID stability: + - Never change or reassign an existing requirement's number. + - New requirements always go at the end of their section. + - If a requirement is removed, keep its number and replace the body with + "REQUIREMENT REMOVED" so external references (tests, review rules) remain valid. + +matchers: + - "**/*REQ-*.md" + +requirements: + rfc-2119-keyword: > + Every numbered requirement statement MUST contain exactly one RFC 2119 + keyword (MUST, MUST NOT, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, + MAY, OPTIONAL, REQUIRED). A requirement without a keyword is ambiguous and + MUST be flagged. + + unique-section-ids: > + Each `### REQ-NNN.M:` section heading MUST have a unique ID within the + file. The NNN portion MUST match the filename's prefix (e.g., sections in + `REQ-001-application-shell.md` MUST all use `REQ-001.X`). + + sequential-numbering: > + Requirements within each section MUST be sequentially numbered starting + from 1, with no gaps or duplicates. Section sub-IDs (the .M part) MUST + also be sequential within the file. + + testability: > + Each requirement MUST be specific enough to be verifiable — either by an + automated test (for concrete, machine-checkable facts) or by a DeepReview + rule (for judgment-based, cross-file concerns). Requirements that cannot + be objectively evaluated MUST be rewritten with concrete criteria. + + overview-section: > + Requirements files MUST begin with a top-level heading matching the + filename prefix, followed by an `## Overview` section that describes the + subsystem's purpose and scope. + + no-orphan-requirements: > + Every MUST or SHALL requirement MUST have a corresponding test, DeepSchema + requirement, or DeepReview rule that validates it. Requirements without any + validation mechanism SHOULD be flagged during review. + + requirement-id-stability: > + Existing requirement numbers MUST NOT be changed or reassigned. New + requirements MUST be appended to the end of their section with the next + sequential number. If a requirement is fully removed, its number MUST + remain in place with the body replaced by "REQUIREMENT REMOVED" so that + all other IDs remain stable and external references do not break. + +verification_bash_command: + - "grep -nE '^[0-9]+\\.' \"$1\" | grep -vE 'MUST|SHALL|SHOULD|MAY|REQUIRED|RECOMMENDED|OPTIONAL' | { if read -r line; then echo \"FAIL: Requirement without RFC 2119 keyword: $line\"; exit 1; fi; }" + +references: + - path: "https://www.ietf.org/rfc/rfc2119.txt" + description: "RFC 2119 — Key words for use in RFCs to Indicate Requirement Levels." diff --git a/tests/unit/deepschema/test_matcher.py b/tests/unit/deepschema/test_matcher.py index 65105f20..352518f9 100644 --- a/tests/unit/deepschema/test_matcher.py +++ b/tests/unit/deepschema/test_matcher.py @@ -132,3 +132,25 @@ def test_skips_anonymous_schema_with_parse_error(self, tmp_path: Path) -> None: # Should not raise — the broken anonymous schema is silently skipped result = get_schemas_for_file_fast("src/app.py", tmp_path) assert result == [] + + def test_returns_both_named_and_anonymous(self, tmp_path: Path) -> None: + """When both a named and anonymous schema apply, both are returned.""" + # Named schema matching *.yml files + schema_dir = tmp_path / ".deepwork" / "schemas" / "yml_files" + schema_dir.mkdir(parents=True) + (schema_dir / "deepschema.yml").write_text( + "matchers:\n - '**/*.yml'\nrequirements:\n generic: 'MUST be valid'\n", + encoding="utf-8", + ) + # Anonymous schema for a specific yml file + src = tmp_path / "src" + src.mkdir() + (src / ".deepschema.config.yml.yml").write_text( + "requirements:\n specific: 'MUST have timeout field'\n", + encoding="utf-8", + ) + result = get_schemas_for_file_fast("src/config.yml", tmp_path) + assert len(result) == 2 + names = {s.name for s in result} + assert "yml_files" in names + assert "config.yml" in names diff --git a/uv.lock b/uv.lock index 74ce5937..b0b2037e 100644 --- a/uv.lock +++ b/uv.lock @@ -478,6 +478,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "pytest-xdist" }, { name = "ruff" }, { name = "types-aiofiles" }, { name = "types-jsonschema" }, @@ -493,6 +494,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-mock" }, + { name = "pytest-xdist" }, { name = "rich" }, ] @@ -509,6 +511,7 @@ requires-dist = [ { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=1.3.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.0.0" }, { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.15.1" }, + { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5.0" }, { name = "pyyaml", specifier = ">=6.0.3" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.8" }, { name = "types-aiofiles", marker = "extra == 'dev'" }, @@ -526,6 +529,7 @@ dev = [ { name = "pytest-asyncio", specifier = ">=1.3.0" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "pytest-mock", specifier = ">=3.15.1" }, + { name = "pytest-xdist", specifier = ">=3.5.0" }, { name = "rich", specifier = ">=14.2.0" }, ] @@ -590,6 +594,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +] + [[package]] name = "fastmcp" version = "3.2.0" @@ -1560,6 +1573,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, ] +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.1"