deepwork 0.5.1__py3-none-any.whl → 0.7.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. deepwork/__init__.py +1 -1
  2. deepwork/cli/hook.py +3 -4
  3. deepwork/cli/install.py +70 -117
  4. deepwork/cli/main.py +2 -2
  5. deepwork/cli/serve.py +133 -0
  6. deepwork/cli/sync.py +93 -58
  7. deepwork/core/adapters.py +91 -102
  8. deepwork/core/generator.py +19 -386
  9. deepwork/core/hooks_syncer.py +1 -1
  10. deepwork/core/parser.py +270 -1
  11. deepwork/hooks/README.md +0 -44
  12. deepwork/hooks/__init__.py +3 -6
  13. deepwork/hooks/check_version.sh +54 -21
  14. deepwork/mcp/__init__.py +23 -0
  15. deepwork/mcp/quality_gate.py +347 -0
  16. deepwork/mcp/schemas.py +263 -0
  17. deepwork/mcp/server.py +253 -0
  18. deepwork/mcp/state.py +422 -0
  19. deepwork/mcp/tools.py +394 -0
  20. deepwork/schemas/job.schema.json +347 -0
  21. deepwork/schemas/job_schema.py +27 -239
  22. deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md +9 -15
  23. deepwork/standard_jobs/deepwork_jobs/job.yml +146 -46
  24. deepwork/standard_jobs/deepwork_jobs/steps/define.md +100 -33
  25. deepwork/standard_jobs/deepwork_jobs/steps/errata.md +154 -0
  26. deepwork/standard_jobs/deepwork_jobs/steps/fix_jobs.md +207 -0
  27. deepwork/standard_jobs/deepwork_jobs/steps/fix_settings.md +177 -0
  28. deepwork/standard_jobs/deepwork_jobs/steps/implement.md +22 -138
  29. deepwork/standard_jobs/deepwork_jobs/steps/iterate.md +221 -0
  30. deepwork/standard_jobs/deepwork_jobs/steps/learn.md +2 -26
  31. deepwork/standard_jobs/deepwork_jobs/steps/test.md +154 -0
  32. deepwork/standard_jobs/deepwork_jobs/templates/job.yml.template +2 -0
  33. deepwork/templates/claude/settings.json +16 -0
  34. deepwork/templates/claude/skill-deepwork.md.jinja +37 -0
  35. deepwork/templates/gemini/skill-deepwork.md.jinja +37 -0
  36. deepwork-0.7.0a1.dist-info/METADATA +317 -0
  37. deepwork-0.7.0a1.dist-info/RECORD +64 -0
  38. deepwork/cli/rules.py +0 -32
  39. deepwork/core/command_executor.py +0 -190
  40. deepwork/core/pattern_matcher.py +0 -271
  41. deepwork/core/rules_parser.py +0 -559
  42. deepwork/core/rules_queue.py +0 -321
  43. deepwork/hooks/rules_check.py +0 -759
  44. deepwork/schemas/rules_schema.py +0 -135
  45. deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md +0 -208
  46. deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.example +0 -86
  47. deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh +0 -38
  48. deepwork/standard_jobs/deepwork_rules/hooks/global_hooks.yml +0 -8
  49. deepwork/standard_jobs/deepwork_rules/hooks/user_prompt_submit.sh +0 -16
  50. deepwork/standard_jobs/deepwork_rules/job.yml +0 -49
  51. deepwork/standard_jobs/deepwork_rules/rules/.gitkeep +0 -13
  52. deepwork/standard_jobs/deepwork_rules/rules/api-documentation-sync.md.example +0 -10
  53. deepwork/standard_jobs/deepwork_rules/rules/readme-documentation.md.example +0 -10
  54. deepwork/standard_jobs/deepwork_rules/rules/security-review.md.example +0 -11
  55. deepwork/standard_jobs/deepwork_rules/rules/skill-md-validation.md +0 -46
  56. deepwork/standard_jobs/deepwork_rules/rules/source-test-pairing.md.example +0 -13
  57. deepwork/standard_jobs/deepwork_rules/steps/define.md +0 -249
  58. deepwork/templates/claude/skill-job-meta.md.jinja +0 -77
  59. deepwork/templates/claude/skill-job-step.md.jinja +0 -235
  60. deepwork/templates/gemini/skill-job-meta.toml.jinja +0 -76
  61. deepwork/templates/gemini/skill-job-step.toml.jinja +0 -162
  62. deepwork-0.5.1.dist-info/METADATA +0 -381
  63. deepwork-0.5.1.dist-info/RECORD +0 -72
  64. {deepwork-0.5.1.dist-info → deepwork-0.7.0a1.dist-info}/WHEEL +0 -0
  65. {deepwork-0.5.1.dist-info → deepwork-0.7.0a1.dist-info}/entry_points.txt +0 -0
  66. {deepwork-0.5.1.dist-info → deepwork-0.7.0a1.dist-info}/licenses/LICENSE.md +0 -0
deepwork/core/adapters.py CHANGED
@@ -55,12 +55,6 @@ class AgentAdapter(ABC):
55
55
  display_name: ClassVar[str]
56
56
  config_dir: ClassVar[str]
57
57
  skills_dir: ClassVar[str] = "skills"
58
- skill_template: ClassVar[str] = "skill-job-step.md.jinja"
59
- meta_skill_template: ClassVar[str] = "skill-job-meta.md.jinja"
60
-
61
- # Instructions for reloading skills after sync (shown to users)
62
- # Subclasses should override with platform-specific instructions.
63
- reload_instructions: ClassVar[str] = "Restart your AI assistant session to use the new skills."
64
58
 
65
59
  # Mapping from generic SkillLifecycleHook to platform-specific event names.
66
60
  # Subclasses should override this to provide platform-specific mappings.
@@ -152,38 +146,6 @@ class AgentAdapter(ABC):
152
146
  raise AdapterError("No project root specified")
153
147
  return root / self.config_dir / self.skills_dir
154
148
 
155
- def get_meta_skill_filename(self, job_name: str) -> str:
156
- """
157
- Get the filename for a job's meta-skill.
158
-
159
- The meta-skill is the primary user interface for a job.
160
- Can be overridden for different file formats.
161
-
162
- Args:
163
- job_name: Name of the job
164
-
165
- Returns:
166
- Meta-skill filename (e.g., "job_name/SKILL.md" for Claude)
167
- """
168
- return f"{job_name}/SKILL.md"
169
-
170
- def get_step_skill_filename(self, job_name: str, step_id: str, exposed: bool = False) -> str:
171
- """
172
- Get the filename for a step skill.
173
-
174
- All step skills use the same filename format. The exposed parameter
175
- is used for template context (user-invocable frontmatter setting).
176
-
177
- Args:
178
- job_name: Name of the job
179
- step_id: ID of the step
180
- exposed: If True, skill is user-invocable (for template context). Default: False.
181
-
182
- Returns:
183
- Skill filename (e.g., "job_name.step_id/SKILL.md" for Claude)
184
- """
185
- return f"{job_name}.{step_id}/SKILL.md"
186
-
187
149
  def detect(self, project_root: Path | None = None) -> bool:
188
150
  """
189
151
  Check if this platform is available in the project.
@@ -260,6 +222,22 @@ class AgentAdapter(ABC):
260
222
  # Default implementation does nothing - subclasses can override
261
223
  return 0
262
224
 
225
+ def register_mcp_server(self, project_path: Path) -> bool:
226
+ """
227
+ Register the DeepWork MCP server with the platform.
228
+
229
+ Args:
230
+ project_path: Path to project root
231
+
232
+ Returns:
233
+ True if server was registered, False if already registered
234
+
235
+ Raises:
236
+ AdapterError: If registration fails
237
+ """
238
+ # Default implementation does nothing - subclasses can override
239
+ return False
240
+
263
241
 
264
242
  def _hook_already_present(hooks: list[dict[str, Any]], script_path: str) -> bool:
265
243
  """Check if a hook with the given script path is already in the list."""
@@ -296,12 +274,6 @@ class ClaudeAdapter(AgentAdapter):
296
274
  display_name = "Claude Code"
297
275
  config_dir = ".claude"
298
276
 
299
- # Claude Code doesn't have a reload command - must restart session
300
- reload_instructions: ClassVar[str] = (
301
- "Type 'exit' to leave your current session, then run "
302
- "'claude --resume' (your history will be maintained)."
303
- )
304
-
305
277
  # Claude Code uses PascalCase event names
306
278
  hook_name_mapping: ClassVar[dict[SkillLifecycleHook, str]] = {
307
279
  SkillLifecycleHook.AFTER_AGENT: "Stop",
@@ -443,13 +415,25 @@ class ClaudeAdapter(AgentAdapter):
443
415
  return True
444
416
  return False
445
417
 
418
+ def _get_settings_template_path(self) -> Path:
419
+ """Get the path to the settings.json template for this adapter."""
420
+ return Path(__file__).parent.parent / "templates" / self.name / "settings.json"
421
+
422
+ def _load_required_permissions(self) -> list[str]:
423
+ """Load required permissions from the settings template file."""
424
+ settings_template = self._get_settings_template_path()
425
+ with open(settings_template, encoding="utf-8") as f:
426
+ template_settings = json.load(f)
427
+ result: list[str] = template_settings["permissions"]["allow"]
428
+ return result
429
+
446
430
  def sync_permissions(self, project_path: Path) -> int:
447
431
  """
448
432
  Sync required permissions to Claude Code settings.json.
449
433
 
450
- Adds permissions for:
451
- - .deepwork/** - full access to deepwork directory
452
- - All deepwork CLI commands (deepwork:*)
434
+ Loads permissions from the settings template file at
435
+ templates/claude/settings.json and merges them into the
436
+ project's .claude/settings.json.
453
437
 
454
438
  Args:
455
439
  project_path: Path to project root
@@ -460,20 +444,7 @@ class ClaudeAdapter(AgentAdapter):
460
444
  Raises:
461
445
  AdapterError: If sync fails
462
446
  """
463
- # Define required permissions for DeepWork functionality
464
- # Uses ./ prefix for paths relative to project root (per Claude Code docs)
465
- required_permissions = [
466
- # Full access to .deepwork directory
467
- "Read(./.deepwork/**)",
468
- "Edit(./.deepwork/**)",
469
- "Write(./.deepwork/**)",
470
- # All deepwork CLI commands
471
- "Bash(deepwork:*)",
472
- # Job scripts that need to be executable
473
- "Bash(./.deepwork/jobs/deepwork_jobs/make_new_job.sh:*)",
474
- ]
475
- # NOTE: When modifying required_permissions, update the test assertion in
476
- # tests/unit/test_adapters.py::TestClaudeAdapter::test_sync_permissions_idempotent
447
+ required_permissions = self._load_required_permissions()
477
448
 
478
449
  # Load settings once, add all permissions, then save once
479
450
  settings = self._load_settings(project_path)
@@ -557,6 +528,65 @@ class ClaudeAdapter(AgentAdapter):
557
528
 
558
529
  return None
559
530
 
531
+ def register_mcp_server(self, project_path: Path) -> bool:
532
+ """
533
+ Register the DeepWork MCP server in .mcp.json at project root.
534
+
535
+ Claude Code reads MCP server configurations from .mcp.json (project scope),
536
+ not from settings.json. This method assumes the `deepwork` command is
537
+ available in the user's PATH.
538
+
539
+ Args:
540
+ project_path: Path to project root
541
+
542
+ Returns:
543
+ True if server was registered or updated, False if no changes needed
544
+
545
+ Raises:
546
+ AdapterError: If registration fails
547
+ """
548
+ mcp_file = project_path / ".mcp.json"
549
+
550
+ # Load existing .mcp.json or create new
551
+ existing_config: dict[str, Any] = {}
552
+ if mcp_file.exists():
553
+ try:
554
+ with open(mcp_file, encoding="utf-8") as f:
555
+ existing_config = json.load(f)
556
+ except (json.JSONDecodeError, OSError) as e:
557
+ raise AdapterError(f"Failed to read .mcp.json: {e}") from e
558
+
559
+ # Initialize mcpServers if not present
560
+ if "mcpServers" not in existing_config:
561
+ existing_config["mcpServers"] = {}
562
+
563
+ # Build the new MCP server config
564
+ # Assume deepwork is available in PATH
565
+ new_server_config = {
566
+ "command": "deepwork",
567
+ "args": ["serve", "--path", "."],
568
+ }
569
+
570
+ # Check if already registered with same config
571
+ existing_server = existing_config["mcpServers"].get("deepwork", {})
572
+ if (
573
+ existing_server.get("command") == new_server_config["command"]
574
+ and existing_server.get("args") == new_server_config["args"]
575
+ ):
576
+ return False
577
+
578
+ # Register or update the DeepWork MCP server
579
+ existing_config["mcpServers"]["deepwork"] = new_server_config
580
+
581
+ # Write .mcp.json
582
+ try:
583
+ with open(mcp_file, "w", encoding="utf-8") as f:
584
+ json.dump(existing_config, f, indent=2)
585
+ except OSError as e:
586
+ raise AdapterError(f"Failed to write .mcp.json: {e}") from e
587
+
588
+ return True
589
+
560
590
 
561
591
  class GeminiAdapter(AgentAdapter):
562
592
  """Adapter for Gemini CLI.
@@ -574,52 +604,11 @@ class GeminiAdapter(AgentAdapter):
574
604
  name = "gemini"
575
605
  display_name = "Gemini CLI"
576
606
  config_dir = ".gemini"
577
- skill_template = "skill-job-step.toml.jinja"
578
- meta_skill_template = "skill-job-meta.toml.jinja"
579
-
580
- # Gemini CLI can reload with /memory refresh
581
- reload_instructions: ClassVar[str] = (
582
- "Run '/memory refresh' to reload skills, or restart your Gemini CLI session."
583
- )
584
607
 
585
608
  # Gemini CLI does NOT support skill-level hooks
586
609
  # Hooks are global/project-level in settings.json, not per-skill
587
610
  hook_name_mapping: ClassVar[dict[SkillLifecycleHook, str]] = {}
588
611
 
589
- def get_meta_skill_filename(self, job_name: str) -> str:
590
- """
591
- Get the filename for a Gemini job's meta-skill.
592
-
593
- Gemini uses TOML files and colon namespacing via subdirectories.
594
- For job "my_job", creates: my_job/index.toml
595
-
596
- Args:
597
- job_name: Name of the job
598
-
599
- Returns:
600
- Meta-skill filename path (e.g., "my_job/index.toml")
601
- """
602
- return f"{job_name}/index.toml"
603
-
604
- def get_step_skill_filename(self, job_name: str, step_id: str, exposed: bool = False) -> str:
605
- """
606
- Get the filename for a Gemini step skill.
607
-
608
- Gemini uses TOML files and colon namespacing via subdirectories.
609
- All step skills use the same filename format. The exposed parameter
610
- is used for template context (user-invocable setting).
611
- For job "my_job" and step "step_one", creates: my_job/step_one.toml
612
-
613
- Args:
614
- job_name: Name of the job
615
- step_id: ID of the step
616
- exposed: If True, skill is user-invocable (for template context). Default: False.
617
-
618
- Returns:
619
- Skill filename path (e.g., "my_job/step_one.toml")
620
- """
621
- return f"{job_name}/{step_id}.toml"
622
-
623
612
  def sync_hooks(self, project_path: Path, hooks: dict[str, list[dict[str, Any]]]) -> int:
624
613
  """
625
614
  Sync hooks to Gemini CLI settings.