monoco-toolkit 0.3.5__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 (59) 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 +39 -5
  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 +104 -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 +65 -37
  33. monoco/features/issue/git_service.py +185 -0
  34. monoco/features/issue/linter.py +291 -62
  35. monoco/features/issue/models.py +91 -14
  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/test_priority_integration.py +1 -0
  40. monoco/features/issue/validator.py +185 -65
  41. monoco/features/memo/__init__.py +4 -0
  42. monoco/features/memo/adapter.py +32 -0
  43. monoco/features/memo/cli.py +112 -0
  44. monoco/features/memo/core.py +146 -0
  45. monoco/features/memo/resources/skills/note_processing_workflow/SKILL.md +140 -0
  46. monoco/features/memo/resources/zh/AGENTS.md +8 -0
  47. monoco/features/memo/resources/zh/SKILL.md +75 -0
  48. monoco/features/spike/resources/skills/research_workflow/SKILL.md +121 -0
  49. monoco/main.py +6 -3
  50. {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
  51. {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +56 -35
  52. monoco/features/scheduler/defaults.py +0 -54
  53. monoco/features/skills/__init__.py +0 -0
  54. monoco/features/skills/core.py +0 -102
  55. /monoco/core/{hooks.py → githooks.py} +0 -0
  56. /monoco/features/{scheduler → agent}/engines.py +0 -0
  57. {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
  58. {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
  59. {monoco_toolkit-0.3.5.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:
@@ -221,6 +232,42 @@ def create_issue_file(
221
232
  if not find_issue_path(issues_root, rel_id):
222
233
  raise ValueError(f"Related issue {rel_id} not found.")
223
234
 
235
+ # Auto-assign default parent for non-epic types if not provided
236
+ if issue_type != IssueType.EPIC and not parent:
237
+ parent = "EPIC-0000"
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
+
224
271
  issue_id = find_next_id(issue_type, issues_root)
225
272
  base_type_dir = get_issue_dir(issue_type, issues_root)
226
273
  target_dir = base_type_dir / status
@@ -264,6 +311,7 @@ def create_issue_file(
264
311
  sprint=sprint,
265
312
  tags=final_tags,
266
313
  opened_at=current_time() if status == IssueStatus.OPEN else None,
314
+ criticality=effective_criticality,
267
315
  )
268
316
 
269
317
  # Enforce lifecycle policies
@@ -399,6 +447,9 @@ def update_issue(
399
447
  related: Optional[List[str]] = None,
400
448
  tags: Optional[List[str]] = None,
401
449
  files: Optional[List[str]] = None,
450
+ criticality: Optional[CriticalityLevel] = None,
451
+ no_commit: bool = False,
452
+ project_root: Optional[Path] = None,
402
453
  ) -> IssueMetadata:
403
454
  path = find_issue_path(issues_root, issue_id)
404
455
  if not path:
@@ -460,6 +511,9 @@ def update_issue(
460
511
  except ValueError:
461
512
  pass
462
513
 
514
+ # Reconstruct temporary metadata for policy validation
515
+ temp_meta = IssueMetadata(**data)
516
+
463
517
  # Use engine to validate the transition
464
518
  engine.validate_transition(
465
519
  from_status=current_status,
@@ -467,6 +521,7 @@ def update_issue(
467
521
  to_status=target_status,
468
522
  to_stage=target_stage,
469
523
  solution=effective_solution,
524
+ meta=temp_meta,
470
525
  )
471
526
 
472
527
  if target_status == "closed":
@@ -532,6 +587,23 @@ def update_issue(
532
587
  if files is not None:
533
588
  data["files"] = files
534
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
+
535
607
  # Lifecycle Hooks
536
608
  # 1. Opened At: If transitioning to OPEN
537
609
  if target_status == IssueStatus.OPEN and current_status != IssueStatus.OPEN:
@@ -614,6 +686,38 @@ def update_issue(
614
686
  if updated_meta.parent:
615
687
  recalculate_parent(issues_root, updated_meta.parent)
616
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
+
617
721
  # Update returned metadata with final absolute path
618
722
  updated_meta.path = str(path.absolute())
619
723
  updated_meta.actions = get_available_actions(updated_meta)