deepwork 0.5.1__py3-none-any.whl → 0.7.0__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.
- deepwork/__init__.py +1 -1
- deepwork/cli/hook.py +3 -4
- deepwork/cli/install.py +70 -117
- deepwork/cli/main.py +2 -2
- deepwork/cli/serve.py +133 -0
- deepwork/cli/sync.py +93 -58
- deepwork/core/adapters.py +91 -102
- deepwork/core/generator.py +19 -386
- deepwork/core/hooks_syncer.py +1 -1
- deepwork/core/parser.py +270 -1
- deepwork/hooks/README.md +0 -44
- deepwork/hooks/__init__.py +3 -6
- deepwork/hooks/check_version.sh +54 -21
- deepwork/mcp/__init__.py +23 -0
- deepwork/mcp/quality_gate.py +347 -0
- deepwork/mcp/schemas.py +263 -0
- deepwork/mcp/server.py +253 -0
- deepwork/mcp/state.py +422 -0
- deepwork/mcp/tools.py +394 -0
- deepwork/schemas/job.schema.json +347 -0
- deepwork/schemas/job_schema.py +27 -239
- deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md +9 -15
- deepwork/standard_jobs/deepwork_jobs/job.yml +146 -46
- deepwork/standard_jobs/deepwork_jobs/steps/define.md +100 -33
- deepwork/standard_jobs/deepwork_jobs/steps/errata.md +154 -0
- deepwork/standard_jobs/deepwork_jobs/steps/fix_jobs.md +207 -0
- deepwork/standard_jobs/deepwork_jobs/steps/fix_settings.md +177 -0
- deepwork/standard_jobs/deepwork_jobs/steps/implement.md +22 -138
- deepwork/standard_jobs/deepwork_jobs/steps/iterate.md +221 -0
- deepwork/standard_jobs/deepwork_jobs/steps/learn.md +2 -26
- deepwork/standard_jobs/deepwork_jobs/steps/test.md +154 -0
- deepwork/standard_jobs/deepwork_jobs/templates/job.yml.template +2 -0
- deepwork/templates/claude/settings.json +16 -0
- deepwork/templates/claude/skill-deepwork.md.jinja +37 -0
- deepwork/templates/gemini/skill-deepwork.md.jinja +37 -0
- deepwork-0.7.0.dist-info/METADATA +317 -0
- deepwork-0.7.0.dist-info/RECORD +64 -0
- deepwork/cli/rules.py +0 -32
- deepwork/core/command_executor.py +0 -190
- deepwork/core/pattern_matcher.py +0 -271
- deepwork/core/rules_parser.py +0 -559
- deepwork/core/rules_queue.py +0 -321
- deepwork/hooks/rules_check.py +0 -759
- deepwork/schemas/rules_schema.py +0 -135
- deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md +0 -208
- deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.example +0 -86
- deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh +0 -38
- deepwork/standard_jobs/deepwork_rules/hooks/global_hooks.yml +0 -8
- deepwork/standard_jobs/deepwork_rules/hooks/user_prompt_submit.sh +0 -16
- deepwork/standard_jobs/deepwork_rules/job.yml +0 -49
- deepwork/standard_jobs/deepwork_rules/rules/.gitkeep +0 -13
- deepwork/standard_jobs/deepwork_rules/rules/api-documentation-sync.md.example +0 -10
- deepwork/standard_jobs/deepwork_rules/rules/readme-documentation.md.example +0 -10
- deepwork/standard_jobs/deepwork_rules/rules/security-review.md.example +0 -11
- deepwork/standard_jobs/deepwork_rules/rules/skill-md-validation.md +0 -46
- deepwork/standard_jobs/deepwork_rules/rules/source-test-pairing.md.example +0 -13
- deepwork/standard_jobs/deepwork_rules/steps/define.md +0 -249
- deepwork/templates/claude/skill-job-meta.md.jinja +0 -77
- deepwork/templates/claude/skill-job-step.md.jinja +0 -235
- deepwork/templates/gemini/skill-job-meta.toml.jinja +0 -76
- deepwork/templates/gemini/skill-job-step.toml.jinja +0 -162
- deepwork-0.5.1.dist-info/METADATA +0 -381
- deepwork-0.5.1.dist-info/RECORD +0 -72
- {deepwork-0.5.1.dist-info → deepwork-0.7.0.dist-info}/WHEEL +0 -0
- {deepwork-0.5.1.dist-info → deepwork-0.7.0.dist-info}/entry_points.txt +0 -0
- {deepwork-0.5.1.dist-info → deepwork-0.7.0.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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
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.
|