monoco-toolkit 0.3.6__py3-none-any.whl → 0.3.9__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 (58) hide show
  1. monoco/cli/workspace.py +1 -1
  2. monoco/core/config.py +51 -0
  3. monoco/core/hooks/__init__.py +19 -0
  4. monoco/core/hooks/base.py +104 -0
  5. monoco/core/hooks/builtin/__init__.py +11 -0
  6. monoco/core/hooks/builtin/git_cleanup.py +266 -0
  7. monoco/core/hooks/builtin/logging_hook.py +78 -0
  8. monoco/core/hooks/context.py +131 -0
  9. monoco/core/hooks/registry.py +222 -0
  10. monoco/core/integrations.py +6 -0
  11. monoco/core/registry.py +2 -0
  12. monoco/core/setup.py +1 -1
  13. monoco/core/skills.py +226 -42
  14. monoco/features/{scheduler → agent}/__init__.py +4 -2
  15. monoco/features/{scheduler → agent}/cli.py +134 -80
  16. monoco/features/{scheduler → agent}/config.py +17 -3
  17. monoco/features/agent/defaults.py +55 -0
  18. monoco/features/agent/flow_skills.py +281 -0
  19. monoco/features/{scheduler → agent}/manager.py +39 -2
  20. monoco/features/{scheduler → agent}/models.py +6 -3
  21. monoco/features/{scheduler → agent}/reliability.py +1 -1
  22. monoco/features/agent/resources/skills/flow_engineer/SKILL.md +94 -0
  23. monoco/features/agent/resources/skills/flow_manager/SKILL.md +88 -0
  24. monoco/features/agent/resources/skills/flow_reviewer/SKILL.md +114 -0
  25. monoco/features/{scheduler → agent}/session.py +36 -1
  26. monoco/features/{scheduler → agent}/worker.py +2 -2
  27. monoco/features/i18n/resources/skills/i18n_scan_workflow/SKILL.md +105 -0
  28. monoco/features/issue/commands.py +427 -21
  29. monoco/features/issue/core.py +100 -0
  30. monoco/features/issue/criticality.py +553 -0
  31. monoco/features/issue/domain/models.py +28 -2
  32. monoco/features/issue/engine/machine.py +70 -13
  33. monoco/features/issue/git_service.py +185 -0
  34. monoco/features/issue/linter.py +291 -62
  35. monoco/features/issue/models.py +49 -2
  36. monoco/features/issue/resources/en/SKILL.md +48 -0
  37. monoco/features/issue/resources/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  38. monoco/features/issue/resources/zh/SKILL.md +50 -0
  39. monoco/features/issue/validator.py +185 -65
  40. monoco/features/memo/__init__.py +2 -1
  41. monoco/features/memo/adapter.py +32 -0
  42. monoco/features/memo/cli.py +36 -14
  43. monoco/features/memo/core.py +59 -0
  44. monoco/features/memo/resources/skills/note_processing_workflow/SKILL.md +140 -0
  45. monoco/features/memo/resources/zh/AGENTS.md +8 -0
  46. monoco/features/memo/resources/zh/SKILL.md +75 -0
  47. monoco/features/spike/resources/skills/research_workflow/SKILL.md +121 -0
  48. monoco/main.py +2 -3
  49. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
  50. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +55 -37
  51. monoco/features/scheduler/defaults.py +0 -54
  52. monoco/features/skills/__init__.py +0 -0
  53. monoco/features/skills/core.py +0 -102
  54. /monoco/core/{hooks.py → githooks.py} +0 -0
  55. /monoco/features/{scheduler → agent}/engines.py +0 -0
  56. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
  57. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
  58. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.9.dist-info}/licenses/LICENSE +0 -0
@@ -15,12 +15,18 @@ from .models import (
15
15
  current_time,
16
16
  generate_uid,
17
17
  )
18
+ from .criticality import (
19
+ CriticalityLevel,
20
+ CriticalityTypeMapping,
21
+ CriticalityInheritanceService,
22
+ )
18
23
  from monoco.core import git
19
24
  from monoco.core.config import get_config, MonocoConfig
20
25
  from monoco.core.lsp import DiagnosticSeverity
21
26
  from .validator import IssueValidator
22
27
 
23
28
  from .engine import get_engine
29
+ from .git_service import IssueGitService
24
30
 
25
31
 
26
32
  def get_prefix_map(issues_root: Path) -> Dict[str, str]:
@@ -137,6 +143,10 @@ def _serialize_metadata(metadata: IssueMetadata) -> str:
137
143
  elif k == "parent":
138
144
  ordered_data[k] = None
139
145
 
146
+ # Add criticality if present
147
+ if "criticality" in data:
148
+ ordered_data["criticality"] = data["criticality"]
149
+
140
150
  # Add remaining
141
151
  for k, v in data.items():
142
152
  if k not in ordered_data:
@@ -211,6 +221,7 @@ def create_issue_file(
211
221
  subdir: Optional[str] = None,
212
222
  sprint: Optional[str] = None,
213
223
  tags: List[str] = [],
224
+ criticality: Optional[CriticalityLevel] = None,
214
225
  ) -> Tuple[IssueMetadata, Path]:
215
226
  # Validation
216
227
  for dep_id in dependencies:
@@ -225,6 +236,38 @@ def create_issue_file(
225
236
  if issue_type != IssueType.EPIC and not parent:
226
237
  parent = "EPIC-0000"
227
238
 
239
+ # Determine criticality
240
+ # 1. Use provided criticality if specified
241
+ # 2. Check parent for inheritance
242
+ # 3. Apply type-based default
243
+ effective_criticality = criticality
244
+
245
+ # Get issue type string for mapping lookup
246
+ issue_type_str = (
247
+ issue_type.value if isinstance(issue_type, IssueType) else str(issue_type)
248
+ )
249
+
250
+ if effective_criticality is None:
251
+ # Check parent inheritance
252
+ if parent:
253
+ parent_path = find_issue_path(issues_root, parent)
254
+ if parent_path:
255
+ parent_meta = parse_issue(parent_path)
256
+ if parent_meta and parent_meta.criticality:
257
+ # Child must inherit at least parent's criticality
258
+ default_type_criticality = CriticalityTypeMapping.get_default(
259
+ issue_type_str
260
+ )
261
+ effective_criticality = (
262
+ CriticalityInheritanceService.resolve_child_criticality(
263
+ parent_meta.criticality, default_type_criticality
264
+ )
265
+ )
266
+
267
+ # Fall back to type-based default
268
+ if effective_criticality is None:
269
+ effective_criticality = CriticalityTypeMapping.get_default(issue_type_str)
270
+
228
271
  issue_id = find_next_id(issue_type, issues_root)
229
272
  base_type_dir = get_issue_dir(issue_type, issues_root)
230
273
  target_dir = base_type_dir / status
@@ -268,6 +311,7 @@ def create_issue_file(
268
311
  sprint=sprint,
269
312
  tags=final_tags,
270
313
  opened_at=current_time() if status == IssueStatus.OPEN else None,
314
+ criticality=effective_criticality,
271
315
  )
272
316
 
273
317
  # Enforce lifecycle policies
@@ -403,6 +447,9 @@ def update_issue(
403
447
  related: Optional[List[str]] = None,
404
448
  tags: Optional[List[str]] = None,
405
449
  files: Optional[List[str]] = None,
450
+ criticality: Optional[CriticalityLevel] = None,
451
+ no_commit: bool = False,
452
+ project_root: Optional[Path] = None,
406
453
  ) -> IssueMetadata:
407
454
  path = find_issue_path(issues_root, issue_id)
408
455
  if not path:
@@ -464,6 +511,9 @@ def update_issue(
464
511
  except ValueError:
465
512
  pass
466
513
 
514
+ # Reconstruct temporary metadata for policy validation
515
+ temp_meta = IssueMetadata(**data)
516
+
467
517
  # Use engine to validate the transition
468
518
  engine.validate_transition(
469
519
  from_status=current_status,
@@ -471,6 +521,7 @@ def update_issue(
471
521
  to_status=target_status,
472
522
  to_stage=target_stage,
473
523
  solution=effective_solution,
524
+ meta=temp_meta,
474
525
  )
475
526
 
476
527
  if target_status == "closed":
@@ -536,6 +587,23 @@ def update_issue(
536
587
  if files is not None:
537
588
  data["files"] = files
538
589
 
590
+ # Criticality update (only through escalation workflow)
591
+ if criticality is not None:
592
+ current_criticality = data.get("criticality")
593
+ if current_criticality:
594
+ current_level = CriticalityLevel(current_criticality)
595
+ # Only allow escalation (increase), never lowering
596
+ if criticality > current_level:
597
+ data["criticality"] = criticality.value
598
+ elif criticality < current_level:
599
+ raise ValueError(
600
+ f"Cannot lower criticality from {current_level.value} to {criticality.value}. "
601
+ "Criticality is immutable and can only be increased through escalation workflow."
602
+ )
603
+ else:
604
+ # Set if not previously set
605
+ data["criticality"] = criticality.value
606
+
539
607
  # Lifecycle Hooks
540
608
  # 1. Opened At: If transitioning to OPEN
541
609
  if target_status == IssueStatus.OPEN and current_status != IssueStatus.OPEN:
@@ -618,6 +686,38 @@ def update_issue(
618
686
  if updated_meta.parent:
619
687
  recalculate_parent(issues_root, updated_meta.parent)
620
688
 
689
+ # Auto-commit issue file changes (FEAT-0115)
690
+ if not no_commit:
691
+ # Determine the action type for commit message
692
+ action = "update"
693
+ if status and status != current_status:
694
+ action = status # "open", "closed", "backlog"
695
+ elif stage and stage != current_stage:
696
+ action = stage # "draft", "doing", "review", "done"
697
+
698
+ # Resolve project root if not provided
699
+ if project_root is None:
700
+ project_root = issues_root.parent
701
+
702
+ # Only auto-commit if we're in a git repo
703
+ git_service = IssueGitService(project_root)
704
+ if git_service.is_git_repository():
705
+ # Determine old path if status changed (file was moved)
706
+ old_path_for_git = None
707
+ if status and status != current_status:
708
+ # The original path before move
709
+ old_path_for_git = find_issue_path(issues_root, issue_id)
710
+
711
+ commit_result = git_service.commit_issue_change(
712
+ issue_id=issue_id,
713
+ action=action,
714
+ issue_file_path=path,
715
+ old_file_path=old_path_for_git if old_path_for_git != path else None,
716
+ no_commit=no_commit,
717
+ )
718
+ # Attach commit result to metadata for optional inspection
719
+ updated_meta.commit_result = commit_result
720
+
621
721
  # Update returned metadata with final absolute path
622
722
  updated_meta.path = str(path.absolute())
623
723
  updated_meta.actions = get_available_actions(updated_meta)