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.
- monoco/cli/workspace.py +1 -1
- monoco/core/config.py +51 -0
- monoco/core/hooks/__init__.py +19 -0
- monoco/core/hooks/base.py +104 -0
- monoco/core/hooks/builtin/__init__.py +11 -0
- monoco/core/hooks/builtin/git_cleanup.py +266 -0
- monoco/core/hooks/builtin/logging_hook.py +78 -0
- monoco/core/hooks/context.py +131 -0
- monoco/core/hooks/registry.py +222 -0
- monoco/core/integrations.py +6 -0
- monoco/core/registry.py +2 -0
- monoco/core/setup.py +1 -1
- monoco/core/skills.py +226 -42
- monoco/features/{scheduler → agent}/__init__.py +4 -2
- monoco/features/{scheduler → agent}/cli.py +134 -80
- monoco/features/{scheduler → agent}/config.py +17 -3
- monoco/features/agent/defaults.py +55 -0
- monoco/features/agent/flow_skills.py +281 -0
- monoco/features/{scheduler → agent}/manager.py +39 -2
- monoco/features/{scheduler → agent}/models.py +6 -3
- monoco/features/{scheduler → agent}/reliability.py +1 -1
- monoco/features/agent/resources/skills/flow_engineer/SKILL.md +94 -0
- monoco/features/agent/resources/skills/flow_manager/SKILL.md +88 -0
- monoco/features/agent/resources/skills/flow_reviewer/SKILL.md +114 -0
- monoco/features/{scheduler → agent}/session.py +39 -5
- monoco/features/{scheduler → agent}/worker.py +2 -2
- monoco/features/i18n/resources/skills/i18n_scan_workflow/SKILL.md +105 -0
- monoco/features/issue/commands.py +427 -21
- monoco/features/issue/core.py +104 -0
- monoco/features/issue/criticality.py +553 -0
- monoco/features/issue/domain/models.py +28 -2
- monoco/features/issue/engine/machine.py +65 -37
- monoco/features/issue/git_service.py +185 -0
- monoco/features/issue/linter.py +291 -62
- monoco/features/issue/models.py +91 -14
- monoco/features/issue/resources/en/SKILL.md +48 -0
- monoco/features/issue/resources/skills/issue_lifecycle_workflow/SKILL.md +159 -0
- monoco/features/issue/resources/zh/SKILL.md +50 -0
- monoco/features/issue/test_priority_integration.py +1 -0
- monoco/features/issue/validator.py +185 -65
- monoco/features/memo/__init__.py +4 -0
- monoco/features/memo/adapter.py +32 -0
- monoco/features/memo/cli.py +112 -0
- monoco/features/memo/core.py +146 -0
- monoco/features/memo/resources/skills/note_processing_workflow/SKILL.md +140 -0
- monoco/features/memo/resources/zh/AGENTS.md +8 -0
- monoco/features/memo/resources/zh/SKILL.md +75 -0
- monoco/features/spike/resources/skills/research_workflow/SKILL.md +121 -0
- monoco/main.py +6 -3
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/METADATA +1 -1
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/RECORD +56 -35
- monoco/features/scheduler/defaults.py +0 -54
- monoco/features/skills/__init__.py +0 -0
- monoco/features/skills/core.py +0 -102
- /monoco/core/{hooks.py → githooks.py} +0 -0
- /monoco/features/{scheduler → agent}/engines.py +0 -0
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.5.dist-info → monoco_toolkit-0.3.9.dist-info}/licenses/LICENSE +0 -0
monoco/features/issue/core.py
CHANGED
|
@@ -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)
|