monoco-toolkit 0.3.6__py3-none-any.whl → 0.3.10__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 (113) hide show
  1. monoco/cli/workspace.py +1 -1
  2. monoco/core/config.py +58 -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/injection.py +63 -29
  11. monoco/core/integrations.py +8 -2
  12. monoco/core/output.py +5 -5
  13. monoco/core/registry.py +9 -1
  14. monoco/core/resource/__init__.py +5 -0
  15. monoco/core/resource/finder.py +98 -0
  16. monoco/core/resource/manager.py +91 -0
  17. monoco/core/resource/models.py +35 -0
  18. monoco/core/resources/en/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
  19. monoco/core/resources/zh/{SKILL.md → skills/monoco_core/SKILL.md} +2 -0
  20. monoco/core/setup.py +1 -1
  21. monoco/core/skill_framework.py +292 -0
  22. monoco/core/skills.py +538 -254
  23. monoco/core/sync.py +73 -1
  24. monoco/core/workflow_converter.py +420 -0
  25. monoco/features/{scheduler → agent}/__init__.py +5 -3
  26. monoco/features/agent/adapter.py +31 -0
  27. monoco/features/agent/apoptosis.py +44 -0
  28. monoco/features/agent/cli.py +296 -0
  29. monoco/features/agent/config.py +96 -0
  30. monoco/features/agent/defaults.py +12 -0
  31. monoco/features/{scheduler → agent}/engines.py +32 -6
  32. monoco/features/agent/flow_skills.py +281 -0
  33. monoco/features/agent/manager.py +91 -0
  34. monoco/features/{scheduler → agent}/models.py +6 -3
  35. monoco/features/agent/resources/atoms/atom-code-dev.yaml +61 -0
  36. monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +73 -0
  37. monoco/features/agent/resources/atoms/atom-knowledge.yaml +55 -0
  38. monoco/features/agent/resources/atoms/atom-review.yaml +60 -0
  39. monoco/features/agent/resources/en/skills/flow_engineer/SKILL.md +94 -0
  40. monoco/features/agent/resources/en/skills/flow_manager/SKILL.md +93 -0
  41. monoco/features/agent/resources/en/skills/flow_planner/SKILL.md +85 -0
  42. monoco/features/agent/resources/en/skills/flow_reviewer/SKILL.md +114 -0
  43. monoco/features/agent/resources/roles/role-engineer.yaml +49 -0
  44. monoco/features/agent/resources/roles/role-manager.yaml +46 -0
  45. monoco/features/agent/resources/roles/role-planner.yaml +46 -0
  46. monoco/features/agent/resources/roles/role-reviewer.yaml +47 -0
  47. monoco/features/agent/resources/workflows/workflow-dev.yaml +83 -0
  48. monoco/features/agent/resources/workflows/workflow-issue-create.yaml +72 -0
  49. monoco/features/agent/resources/workflows/workflow-review.yaml +94 -0
  50. monoco/features/agent/resources/zh/skills/flow_engineer/SKILL.md +94 -0
  51. monoco/features/agent/resources/zh/skills/flow_manager/SKILL.md +88 -0
  52. monoco/features/agent/resources/zh/skills/flow_planner/SKILL.md +259 -0
  53. monoco/features/agent/resources/zh/skills/flow_reviewer/SKILL.md +137 -0
  54. monoco/features/{scheduler → agent}/session.py +36 -1
  55. monoco/features/{scheduler → agent}/worker.py +40 -4
  56. monoco/features/glossary/adapter.py +31 -0
  57. monoco/features/glossary/config.py +5 -0
  58. monoco/features/glossary/resources/en/AGENTS.md +29 -0
  59. monoco/features/glossary/resources/en/skills/monoco_glossary/SKILL.md +35 -0
  60. monoco/features/glossary/resources/zh/AGENTS.md +29 -0
  61. monoco/features/glossary/resources/zh/skills/monoco_glossary/SKILL.md +35 -0
  62. monoco/features/i18n/resources/en/skills/i18n_scan_workflow/SKILL.md +105 -0
  63. monoco/features/i18n/resources/en/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
  64. monoco/features/i18n/resources/zh/skills/i18n_scan_workflow/SKILL.md +105 -0
  65. monoco/features/i18n/resources/zh/{SKILL.md → skills/monoco_i18n/SKILL.md} +2 -0
  66. monoco/features/issue/commands.py +427 -21
  67. monoco/features/issue/core.py +140 -1
  68. monoco/features/issue/criticality.py +553 -0
  69. monoco/features/issue/domain/models.py +28 -2
  70. monoco/features/issue/engine/machine.py +75 -15
  71. monoco/features/issue/git_service.py +185 -0
  72. monoco/features/issue/linter.py +291 -62
  73. monoco/features/issue/models.py +50 -2
  74. monoco/features/issue/resources/en/skills/issue_create_workflow/SKILL.md +167 -0
  75. monoco/features/issue/resources/en/skills/issue_develop_workflow/SKILL.md +224 -0
  76. monoco/features/issue/resources/en/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  77. monoco/features/issue/resources/en/skills/issue_refine_workflow/SKILL.md +203 -0
  78. monoco/features/issue/resources/en/{SKILL.md → skills/monoco_issue/SKILL.md} +50 -0
  79. monoco/features/issue/resources/zh/skills/issue_create_workflow/SKILL.md +167 -0
  80. monoco/features/issue/resources/zh/skills/issue_develop_workflow/SKILL.md +224 -0
  81. monoco/features/issue/resources/zh/skills/issue_lifecycle_workflow/SKILL.md +159 -0
  82. monoco/features/issue/resources/zh/skills/issue_refine_workflow/SKILL.md +203 -0
  83. monoco/features/issue/resources/zh/{SKILL.md → skills/monoco_issue/SKILL.md} +52 -0
  84. monoco/features/issue/validator.py +185 -65
  85. monoco/features/memo/__init__.py +2 -1
  86. monoco/features/memo/adapter.py +32 -0
  87. monoco/features/memo/cli.py +36 -14
  88. monoco/features/memo/core.py +59 -0
  89. monoco/features/memo/resources/en/skills/monoco_memo/SKILL.md +77 -0
  90. monoco/features/memo/resources/en/skills/note_processing_workflow/SKILL.md +140 -0
  91. monoco/features/memo/resources/zh/AGENTS.md +8 -0
  92. monoco/features/memo/resources/zh/skills/monoco_memo/SKILL.md +77 -0
  93. monoco/features/memo/resources/zh/skills/note_processing_workflow/SKILL.md +140 -0
  94. monoco/features/spike/resources/en/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
  95. monoco/features/spike/resources/en/skills/research_workflow/SKILL.md +121 -0
  96. monoco/features/spike/resources/zh/{SKILL.md → skills/monoco_spike/SKILL.md} +2 -0
  97. monoco/features/spike/resources/zh/skills/research_workflow/SKILL.md +121 -0
  98. monoco/main.py +2 -3
  99. monoco_toolkit-0.3.10.dist-info/METADATA +124 -0
  100. monoco_toolkit-0.3.10.dist-info/RECORD +156 -0
  101. monoco/features/scheduler/cli.py +0 -285
  102. monoco/features/scheduler/config.py +0 -68
  103. monoco/features/scheduler/defaults.py +0 -54
  104. monoco/features/scheduler/manager.py +0 -49
  105. monoco/features/scheduler/reliability.py +0 -106
  106. monoco/features/skills/core.py +0 -102
  107. monoco_toolkit-0.3.6.dist-info/METADATA +0 -127
  108. monoco_toolkit-0.3.6.dist-info/RECORD +0 -97
  109. /monoco/core/{hooks.py → githooks.py} +0 -0
  110. /monoco/features/{skills → glossary}/__init__.py +0 -0
  111. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/WHEEL +0 -0
  112. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.dist-info}/entry_points.txt +0 -0
  113. {monoco_toolkit-0.3.6.dist-info → monoco_toolkit-0.3.10.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,13 +511,17 @@ 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
- engine.validate_transition(
518
+ transition = engine.validate_transition(
469
519
  from_status=current_status,
470
520
  from_stage=current_stage,
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:
@@ -591,6 +659,8 @@ def update_issue(
591
659
  path.write_text(new_content)
592
660
 
593
661
  # 3. Handle physical move if status changed
662
+ # Save old path before move for git tracking
663
+ old_path_before_move = path
594
664
  if status and status != current_status:
595
665
  # Move file
596
666
  prefix = issue_id.split("-")[0].upper()
@@ -618,12 +688,81 @@ def update_issue(
618
688
  if updated_meta.parent:
619
689
  recalculate_parent(issues_root, updated_meta.parent)
620
690
 
691
+ # Auto-commit issue file changes (FEAT-0115)
692
+ if not no_commit:
693
+ # Determine the action type for commit message
694
+ action = "update"
695
+ if status and status != current_status:
696
+ action = status # "open", "closed", "backlog"
697
+ elif stage and stage != current_stage:
698
+ action = stage # "draft", "doing", "review", "done"
699
+
700
+ # Resolve project root if not provided
701
+ if project_root is None:
702
+ project_root = issues_root.parent
703
+
704
+ # Only auto-commit if we're in a git repo
705
+ git_service = IssueGitService(project_root)
706
+ if git_service.is_git_repository():
707
+ # Use the saved old path before file move for git tracking
708
+ old_path_for_git = None
709
+ if status and status != current_status and old_path_before_move != path:
710
+ old_path_for_git = old_path_before_move
711
+
712
+ commit_result = git_service.commit_issue_change(
713
+ issue_id=issue_id,
714
+ action=action,
715
+ issue_file_path=path,
716
+ old_file_path=old_path_for_git,
717
+ no_commit=no_commit,
718
+ )
719
+ # Attach commit result to metadata for optional inspection
720
+ updated_meta.commit_result = commit_result
721
+
621
722
  # Update returned metadata with final absolute path
622
723
  updated_meta.path = str(path.absolute())
623
724
  updated_meta.actions = get_available_actions(updated_meta)
725
+
726
+ # Execute Post Actions (Trigger)
727
+ if transition and hasattr(transition, "post_actions") and transition.post_actions:
728
+ _execute_post_actions(transition.post_actions, updated_meta)
729
+
624
730
  return updated_meta
625
731
 
626
732
 
733
+ def _execute_post_actions(actions: List[str], meta: IssueMetadata):
734
+ """
735
+ Execute a list of shell commands as post-actions.
736
+ Supports template substitution with issue metadata.
737
+ """
738
+ import shlex
739
+ import subprocess
740
+ from rich.console import Console
741
+
742
+ console = Console()
743
+ data = meta.model_dump(mode="json")
744
+
745
+ for action in actions:
746
+ try:
747
+ # Safe template substitution
748
+ cmd = action.format(**data)
749
+ except KeyError as e:
750
+ console.print(f"[yellow]Trigger Warning:[/yellow] Missing key for template '{action}': {e}")
751
+ continue
752
+
753
+ console.print(f"[bold cyan]Triggering:[/bold cyan] {cmd}")
754
+
755
+ args = shlex.split(cmd)
756
+
757
+ try:
758
+ # Run in foreground to allow interaction if needed (e.g. agent output)
759
+ subprocess.run(args, check=True)
760
+ except subprocess.CalledProcessError as e:
761
+ console.print(f"[red]Trigger Failed:[/red] Command '{cmd}' exited with code {e.returncode}")
762
+ except Exception as e:
763
+ console.print(f"[red]Trigger Error:[/red] {e}")
764
+
765
+
627
766
  def start_issue_isolation(
628
767
  issues_root: Path, issue_id: str, mode: str, project_root: Path
629
768
  ) -> IssueMetadata: