monoco-toolkit 0.3.12__py3-none-any.whl → 0.4.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.
- monoco/core/automation/__init__.py +0 -11
- monoco/core/automation/handlers.py +108 -26
- monoco/core/config.py +28 -10
- monoco/core/daemon/__init__.py +5 -0
- monoco/core/daemon/pid.py +290 -0
- monoco/core/injection.py +86 -8
- monoco/core/integrations.py +0 -24
- monoco/core/router/__init__.py +1 -39
- monoco/core/router/action.py +3 -142
- monoco/core/scheduler/events.py +28 -2
- monoco/core/setup.py +9 -0
- monoco/core/sync.py +199 -4
- monoco/core/watcher/__init__.py +6 -0
- monoco/core/watcher/base.py +18 -1
- monoco/core/watcher/im.py +460 -0
- monoco/core/watcher/memo.py +40 -48
- monoco/daemon/app.py +3 -60
- monoco/daemon/commands.py +459 -25
- monoco/daemon/scheduler.py +1 -16
- monoco/daemon/services.py +15 -0
- monoco/features/agent/resources/en/AGENTS.md +14 -14
- monoco/features/agent/resources/en/skills/monoco_role_engineer/SKILL.md +101 -0
- monoco/features/agent/resources/en/skills/monoco_role_manager/SKILL.md +95 -0
- monoco/features/agent/resources/en/skills/monoco_role_planner/SKILL.md +177 -0
- monoco/features/agent/resources/en/skills/monoco_role_reviewer/SKILL.md +139 -0
- monoco/features/agent/resources/zh/skills/monoco_role_engineer/SKILL.md +101 -0
- monoco/features/agent/resources/zh/skills/monoco_role_manager/SKILL.md +95 -0
- monoco/features/agent/resources/zh/skills/monoco_role_planner/SKILL.md +177 -0
- monoco/features/agent/resources/zh/skills/monoco_role_reviewer/SKILL.md +139 -0
- monoco/features/hooks/__init__.py +61 -6
- monoco/features/hooks/commands.py +281 -271
- monoco/features/hooks/dispatchers/__init__.py +23 -0
- monoco/features/hooks/dispatchers/agent_dispatcher.py +486 -0
- monoco/features/hooks/dispatchers/git_dispatcher.py +478 -0
- monoco/features/hooks/manager.py +357 -0
- monoco/features/hooks/models.py +262 -0
- monoco/features/hooks/parser.py +322 -0
- monoco/features/hooks/universal_interceptor.py +503 -0
- monoco/features/im/__init__.py +67 -0
- monoco/features/im/core.py +782 -0
- monoco/features/im/models.py +311 -0
- monoco/features/issue/commands.py +65 -50
- monoco/features/issue/core.py +199 -99
- monoco/features/issue/domain_commands.py +0 -19
- monoco/features/issue/resources/en/AGENTS.md +17 -122
- monoco/features/issue/resources/hooks/agent/before-tool.sh +102 -0
- monoco/features/issue/resources/hooks/agent/session-start.sh +88 -0
- monoco/features/issue/resources/hooks/{post-checkout.sh → git/git-post-checkout.sh} +10 -9
- monoco/features/issue/resources/hooks/git/git-pre-commit.sh +31 -0
- monoco/features/issue/resources/hooks/{pre-push.sh → git/git-pre-push.sh} +7 -13
- monoco/features/issue/resources/zh/AGENTS.md +18 -123
- monoco/features/memo/cli.py +15 -64
- monoco/features/memo/core.py +6 -34
- monoco/features/memo/models.py +24 -15
- monoco/features/memo/resources/en/AGENTS.md +31 -0
- monoco/features/memo/resources/zh/AGENTS.md +28 -5
- monoco/main.py +5 -3
- {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/METADATA +1 -1
- monoco_toolkit-0.4.0.dist-info/RECORD +170 -0
- monoco/core/automation/config.py +0 -338
- monoco/core/execution.py +0 -67
- monoco/core/executor/__init__.py +0 -38
- monoco/core/executor/agent_action.py +0 -254
- monoco/core/executor/git_action.py +0 -303
- monoco/core/executor/im_action.py +0 -309
- monoco/core/executor/pytest_action.py +0 -218
- monoco/core/router/router.py +0 -392
- monoco/features/agent/resources/atoms/atom-code-dev.yaml +0 -61
- monoco/features/agent/resources/atoms/atom-issue-lifecycle.yaml +0 -73
- monoco/features/agent/resources/atoms/atom-knowledge.yaml +0 -55
- monoco/features/agent/resources/atoms/atom-review.yaml +0 -60
- monoco/features/agent/resources/en/skills/monoco_atom_core/SKILL.md +0 -99
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_manager/SKILL.md +0 -93
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_planner/SKILL.md +0 -85
- monoco/features/agent/resources/en/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -114
- monoco/features/agent/resources/workflows/workflow-dev.yaml +0 -83
- monoco/features/agent/resources/workflows/workflow-issue-create.yaml +0 -72
- monoco/features/agent/resources/workflows/workflow-review.yaml +0 -94
- monoco/features/agent/resources/zh/roles/monoco_role_engineer.yaml +0 -49
- monoco/features/agent/resources/zh/roles/monoco_role_manager.yaml +0 -46
- monoco/features/agent/resources/zh/roles/monoco_role_planner.yaml +0 -46
- monoco/features/agent/resources/zh/roles/monoco_role_reviewer.yaml +0 -47
- monoco/features/agent/resources/zh/skills/monoco_atom_core/SKILL.md +0 -99
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_engineer/SKILL.md +0 -94
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_manager/SKILL.md +0 -88
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_planner/SKILL.md +0 -259
- monoco/features/agent/resources/zh/skills/monoco_workflow_agent_reviewer/SKILL.md +0 -137
- monoco/features/artifact/resources/zh/skills/monoco_atom_artifact/SKILL.md +0 -278
- monoco/features/glossary/resources/en/skills/monoco_atom_glossary/SKILL.md +0 -35
- monoco/features/glossary/resources/zh/skills/monoco_atom_glossary/SKILL.md +0 -35
- monoco/features/hooks/adapter.py +0 -67
- monoco/features/hooks/core.py +0 -441
- monoco/features/i18n/resources/en/skills/monoco_atom_i18n/SKILL.md +0 -96
- monoco/features/i18n/resources/en/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
- monoco/features/i18n/resources/zh/skills/monoco_atom_i18n/SKILL.md +0 -96
- monoco/features/i18n/resources/zh/skills/monoco_workflow_i18n_scan/SKILL.md +0 -105
- monoco/features/issue/resources/en/skills/monoco_atom_issue/SKILL.md +0 -165
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_development/SKILL.md +0 -224
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_management/SKILL.md +0 -159
- monoco/features/issue/resources/en/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
- monoco/features/issue/resources/hooks/pre-commit.sh +0 -41
- monoco/features/issue/resources/zh/skills/monoco_atom_issue_lifecycle/SKILL.md +0 -190
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_creation/SKILL.md +0 -167
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_development/SKILL.md +0 -224
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_management/SKILL.md +0 -159
- monoco/features/issue/resources/zh/skills/monoco_workflow_issue_refinement/SKILL.md +0 -203
- monoco/features/memo/resources/en/skills/monoco_atom_memo/SKILL.md +0 -77
- monoco/features/memo/resources/en/skills/monoco_workflow_note_processing/SKILL.md +0 -140
- monoco/features/memo/resources/zh/skills/monoco_atom_memo/SKILL.md +0 -77
- monoco/features/memo/resources/zh/skills/monoco_workflow_note_processing/SKILL.md +0 -140
- monoco/features/spike/resources/en/skills/monoco_atom_spike/SKILL.md +0 -76
- monoco/features/spike/resources/en/skills/monoco_workflow_research/SKILL.md +0 -121
- monoco/features/spike/resources/zh/skills/monoco_atom_spike/SKILL.md +0 -76
- monoco/features/spike/resources/zh/skills/monoco_workflow_research/SKILL.md +0 -121
- monoco_toolkit-0.3.12.dist-info/RECORD +0 -202
- {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/WHEEL +0 -0
- {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/entry_points.txt +0 -0
- {monoco_toolkit-0.3.12.dist-info → monoco_toolkit-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# JIT Hook: before-tool for Issue workflow compliance
|
|
3
|
+
# Triggers: Bash, WriteFile tool calls
|
|
4
|
+
# Purpose: Check branch compliance before code modifications
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
type: agent
|
|
8
|
+
provider: claude-code
|
|
9
|
+
event: before-tool
|
|
10
|
+
matcher: ["Bash", "WriteFile"]
|
|
11
|
+
priority: 10
|
|
12
|
+
description: "Check code modification compliance (branch, sync-files)"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Get current branch
|
|
16
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
17
|
+
|
|
18
|
+
# Check if we're on a protected branch
|
|
19
|
+
if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
|
|
20
|
+
cat << 'EOF'
|
|
21
|
+
{
|
|
22
|
+
"decision": "ask",
|
|
23
|
+
"reason": "Protected branch detected",
|
|
24
|
+
"message": "🛑 You are currently on the main/master branch. Direct modifications are prohibited.",
|
|
25
|
+
"metadata": {
|
|
26
|
+
"additionalContext": {
|
|
27
|
+
"current_branch": "main",
|
|
28
|
+
"reminders": [
|
|
29
|
+
"Use 'monoco issue start <ID> --branch' to create a feature branch",
|
|
30
|
+
"Never modify code directly on main/master branch"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
EOF
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Check if branch name follows feature branch pattern (contains FEAT-, FIX-, CHORE-)
|
|
40
|
+
if [[ "$CURRENT_BRANCH" =~ (FEAT|FIX|CHORE)-[0-9]+ ]]; then
|
|
41
|
+
ISSUE_ID="${BASH_REMATCH[0]}"
|
|
42
|
+
|
|
43
|
+
# Check for uncommitted changes that might need sync-files
|
|
44
|
+
if git diff --quiet HEAD 2>/dev/null; then
|
|
45
|
+
# No uncommitted changes
|
|
46
|
+
cat << EOF
|
|
47
|
+
{
|
|
48
|
+
"decision": "allow",
|
|
49
|
+
"reason": "Branch check passed",
|
|
50
|
+
"message": "✅ Current in feature branch: $CURRENT_BRANCH",
|
|
51
|
+
"metadata": {
|
|
52
|
+
"additionalContext": {
|
|
53
|
+
"current_issue": "$ISSUE_ID",
|
|
54
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
55
|
+
"reminders": [
|
|
56
|
+
"Run 'monoco issue sync-files' after modifying files",
|
|
57
|
+
"Run 'monoco issue submit $ISSUE_ID' when ready for review"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
EOF
|
|
63
|
+
else
|
|
64
|
+
# Has uncommitted changes
|
|
65
|
+
cat << EOF
|
|
66
|
+
{
|
|
67
|
+
"decision": "allow",
|
|
68
|
+
"reason": "Branch check passed with pending changes",
|
|
69
|
+
"message": "⚠️ You have uncommitted changes. Remember to run sync-files before submit.",
|
|
70
|
+
"metadata": {
|
|
71
|
+
"additionalContext": {
|
|
72
|
+
"current_issue": "$ISSUE_ID",
|
|
73
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
74
|
+
"uncommitted_changes": true,
|
|
75
|
+
"reminders": [
|
|
76
|
+
"Run 'monoco issue sync-files' to update files field",
|
|
77
|
+
"Run 'monoco issue submit $ISSUE_ID' when ready for review"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
EOF
|
|
83
|
+
fi
|
|
84
|
+
else
|
|
85
|
+
# Not on a feature branch
|
|
86
|
+
cat << EOF
|
|
87
|
+
{
|
|
88
|
+
"decision": "ask",
|
|
89
|
+
"reason": "Not on feature branch",
|
|
90
|
+
"message": "⚠️ You are on '$CURRENT_BRANCH' which does not appear to be a feature branch.",
|
|
91
|
+
"metadata": {
|
|
92
|
+
"additionalContext": {
|
|
93
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
94
|
+
"reminders": [
|
|
95
|
+
"Use 'monoco issue start <ID> --branch' to work on an Issue",
|
|
96
|
+
"Or ensure your branch name contains the Issue ID (e.g., FEAT-0123-xxx)"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
EOF
|
|
102
|
+
fi
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# JIT Hook: session-start for Issue context injection
|
|
3
|
+
# Triggers: At the beginning of agent session
|
|
4
|
+
# Purpose: Inject current Issue context into agent session
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
type: agent
|
|
8
|
+
provider: claude-code
|
|
9
|
+
event: session-start
|
|
10
|
+
priority: 5
|
|
11
|
+
description: "Inject current Issue context into session"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Get current branch
|
|
15
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
16
|
+
|
|
17
|
+
# Try to extract Issue ID from branch name
|
|
18
|
+
if [[ "$CURRENT_BRANCH" =~ (FEAT|FIX|CHORE|EPIC)-[0-9]+ ]]; then
|
|
19
|
+
ISSUE_ID="${BASH_REMATCH[0]}"
|
|
20
|
+
|
|
21
|
+
# Try to find the Issue file
|
|
22
|
+
ISSUE_FILE=$(find /Users/indenscale/Documents/Projects/Monoco/Toolkit/Issues -name "${ISSUE_ID}*.md" 2>/dev/null | head -1)
|
|
23
|
+
|
|
24
|
+
if [[ -n "$ISSUE_FILE" && -f "$ISSUE_FILE" ]]; then
|
|
25
|
+
# Extract issue metadata from front matter
|
|
26
|
+
ISSUE_STATUS=$(grep -E "^status:" "$ISSUE_FILE" | head -1 | cut -d: -f2 | tr -d ' ' || echo "unknown")
|
|
27
|
+
ISSUE_STAGE=$(grep -E "^stage:" "$ISSUE_FILE" | head -1 | cut -d: -f2 | tr -d ' ' || echo "unknown")
|
|
28
|
+
ISSUE_TITLE=$(grep -E "^title:" "$ISSUE_FILE" | head -1 | cut -d: -f2- | sed 's/^ *//' || echo "unknown")
|
|
29
|
+
|
|
30
|
+
cat << EOF
|
|
31
|
+
{
|
|
32
|
+
"decision": "allow",
|
|
33
|
+
"reason": "Issue context loaded",
|
|
34
|
+
"message": "📋 Active Issue: $ISSUE_ID - $ISSUE_TITLE",
|
|
35
|
+
"metadata": {
|
|
36
|
+
"additionalContext": {
|
|
37
|
+
"current_issue": "$ISSUE_ID",
|
|
38
|
+
"issue_title": "$ISSUE_TITLE",
|
|
39
|
+
"issue_status": "$ISSUE_STATUS",
|
|
40
|
+
"issue_stage": "$ISSUE_STAGE",
|
|
41
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
42
|
+
"reminders": [
|
|
43
|
+
"Issue: $ISSUE_ID ($ISSUE_STATUS / $ISSUE_STAGE)",
|
|
44
|
+
"Run 'monoco issue sync-files' after file modifications",
|
|
45
|
+
"Run 'monoco issue submit $ISSUE_ID' when ready for review"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
EOF
|
|
51
|
+
else
|
|
52
|
+
# Issue file not found
|
|
53
|
+
cat << EOF
|
|
54
|
+
{
|
|
55
|
+
"decision": "allow",
|
|
56
|
+
"reason": "Partial context loaded",
|
|
57
|
+
"message": "📋 Detected Issue ID: $ISSUE_ID from branch",
|
|
58
|
+
"metadata": {
|
|
59
|
+
"additionalContext": {
|
|
60
|
+
"current_issue": "$ISSUE_ID",
|
|
61
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
62
|
+
"reminders": [
|
|
63
|
+
"Working on: $ISSUE_ID",
|
|
64
|
+
"Run 'monoco issue sync-files' after file modifications"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
EOF
|
|
70
|
+
fi
|
|
71
|
+
else
|
|
72
|
+
# Not on a feature branch
|
|
73
|
+
cat << EOF
|
|
74
|
+
{
|
|
75
|
+
"decision": "allow",
|
|
76
|
+
"reason": "No active Issue detected",
|
|
77
|
+
"message": "ℹ️ No active Issue detected from branch name",
|
|
78
|
+
"metadata": {
|
|
79
|
+
"additionalContext": {
|
|
80
|
+
"current_branch": "$CURRENT_BRANCH",
|
|
81
|
+
"reminders": [
|
|
82
|
+
"To start working: monoco issue start <ID> --branch"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
EOF
|
|
88
|
+
fi
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
# ---
|
|
3
|
+
# type: git
|
|
4
|
+
# event: post-checkout
|
|
5
|
+
# description: "Sync issue status and files after branch checkout"
|
|
6
|
+
# priority: 100
|
|
7
|
+
# ---
|
|
6
8
|
|
|
7
9
|
# Get the previous and current HEAD
|
|
8
10
|
PREVIOUS_HEAD="$1"
|
|
@@ -11,29 +13,28 @@ BRANCH_SWITCH="$3" # 1 if branch switch, 0 if file checkout
|
|
|
11
13
|
|
|
12
14
|
# Only sync on actual branch switches, not file checkouts
|
|
13
15
|
if [ "$BRANCH_SWITCH" != "1" ]; then
|
|
14
|
-
echo "[Monoco] File checkout detected, skipping issue sync."
|
|
15
16
|
exit 0
|
|
16
17
|
fi
|
|
17
18
|
|
|
19
|
+
echo "[Monoco] Syncing issue status after branch checkout..."
|
|
20
|
+
|
|
18
21
|
# Get current branch name
|
|
19
22
|
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
|
|
20
23
|
|
|
21
24
|
echo "[Monoco] Switched to branch: $CURRENT_BRANCH"
|
|
22
25
|
|
|
23
26
|
# Try to extract issue ID from branch name
|
|
24
|
-
# Common patterns: FEAT-123, feat/FEAT-123, feature/FEAT-123, fix/FEAT-123
|
|
25
27
|
ISSUE_ID=$(echo "$CURRENT_BRANCH" | grep -oE '[A-Z]+-[0-9]+' | head -1)
|
|
26
28
|
|
|
27
29
|
if [ -n "$ISSUE_ID" ]; then
|
|
28
30
|
echo "[Monoco] Detected issue ID from branch: $ISSUE_ID"
|
|
29
|
-
|
|
30
31
|
# Check if issue exists and update its isolation ref if needed
|
|
31
|
-
|
|
32
|
+
monoco issue sync-isolation "$ISSUE_ID" --branch "$CURRENT_BRANCH" 2>/dev/null || true
|
|
32
33
|
fi
|
|
33
34
|
|
|
34
35
|
# Run general sync to ensure files field is up to date
|
|
35
36
|
echo "[Monoco] Running issue file sync..."
|
|
36
|
-
|
|
37
|
+
monoco issue sync-files 2>/dev/null || true
|
|
37
38
|
|
|
38
39
|
echo "[Monoco] Issue sync complete."
|
|
39
40
|
exit 0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# ---
|
|
3
|
+
# type: git
|
|
4
|
+
# event: pre-commit
|
|
5
|
+
# matcher:
|
|
6
|
+
# - "Issues/**/*.md"
|
|
7
|
+
# description: "Lint staged Issue files for integrity"
|
|
8
|
+
# priority: 100
|
|
9
|
+
# ---
|
|
10
|
+
|
|
11
|
+
echo "[Monoco] Checking Issue integrity..."
|
|
12
|
+
|
|
13
|
+
# Get the list of staged Issue files
|
|
14
|
+
STAGED_ISSUES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^Issues/.*\.md$' || true)
|
|
15
|
+
|
|
16
|
+
if [ -z "$STAGED_ISSUES" ]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Run lint on staged Issue files
|
|
21
|
+
echo "[Monoco] Running lint on staged Issue files..."
|
|
22
|
+
|
|
23
|
+
for file in $STAGED_ISSUES; do
|
|
24
|
+
monoco issue lint "$file"
|
|
25
|
+
if [ $? -ne 0 ]; then
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
echo "[Monoco] Issue lint passed."
|
|
31
|
+
exit 0
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
#
|
|
3
|
-
#
|
|
2
|
+
# ---
|
|
3
|
+
# type: git
|
|
4
|
+
# event: pre-push
|
|
5
|
+
# description: "Check for incomplete critical issues before pushing"
|
|
6
|
+
# priority: 100
|
|
7
|
+
# ---
|
|
4
8
|
|
|
5
9
|
echo "[Monoco] Checking critical issues before push..."
|
|
6
10
|
|
|
7
|
-
# Get the list of commits being pushed
|
|
8
|
-
# $1 = remote name, $2 = remote url (if available)
|
|
9
|
-
REMOTE="$1"
|
|
10
|
-
URL="$2"
|
|
11
|
-
|
|
12
|
-
# Check for high/critical issues that are not closed
|
|
13
|
-
# This uses monoco issue query to find open issues with high/critical criticality
|
|
14
|
-
echo "[Monoco] Scanning for incomplete critical issues..."
|
|
15
|
-
|
|
16
11
|
# Run the check using monoco command
|
|
17
|
-
|
|
12
|
+
monoco issue check-critical --fail-on-warning
|
|
18
13
|
CHECK_EXIT=$?
|
|
19
14
|
|
|
20
15
|
if [ $CHECK_EXIT -eq 2 ]; then
|
|
@@ -27,7 +22,6 @@ elif [ $CHECK_EXIT -eq 1 ]; then
|
|
|
27
22
|
echo "[Monoco] ⚠️ High priority issues found."
|
|
28
23
|
echo "[Monoco] Use --force-push to bypass this warning (not recommended)."
|
|
29
24
|
# For now, we allow the push but warn
|
|
30
|
-
# To block, change the exit code to 1
|
|
31
25
|
exit 0
|
|
32
26
|
fi
|
|
33
27
|
|
|
@@ -1,131 +1,26 @@
|
|
|
1
|
-
# Issue 管理
|
|
1
|
+
# Issue 管理
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
使用 `monoco issue` 管理任务。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **创建**: `monoco issue create <type> -t "标题"` (类型: epic, feature, chore, fix)
|
|
5
|
+
- **创建**: `monoco issue create <type> -t "标题"`
|
|
8
6
|
- **状态**: `monoco issue open|close|backlog <id>`
|
|
9
|
-
- **检查**: `monoco issue lint`
|
|
7
|
+
- **检查**: `monoco issue lint`
|
|
10
8
|
- **生命周期**: `monoco issue start|submit|delete <id>`
|
|
11
|
-
- **上下文同步**: `monoco issue sync-files [id]`
|
|
12
|
-
- **结构**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (如 `Issues/Features/open/`)
|
|
13
|
-
- **强制规则**:
|
|
14
|
-
1. **先有 Issue**: 在进行任何调研、设计或 Draft 之前,必须先使用 `monoco issue create` 创建 Issue。
|
|
15
|
-
2. **标题**: 必须包含 `## {ID}: {Title}` 标题(与 Front Matter 一致)。
|
|
16
|
-
3. **内容**: 至少 2 个 Checkbox,使用 `- [ ]`, `- [x]`, `- [-]`, `- [/]`。
|
|
17
|
-
4. **评审**: `review`/`done` 阶段必须包含 `## Review Comments` 章节且内容不为空。
|
|
18
|
-
5. **环境策略**:
|
|
19
|
-
- 必须使用 `monoco issue start --branch` 创建 Feature 分支。
|
|
20
|
-
- 🛑 **禁止**直接在 `main`/`master` 分支修改代码 (Linter 会报错)。
|
|
21
|
-
- **清理时机**: 环境清理仅应在 `close` 时执行。**禁止**在 `submit` 阶段清理环境。
|
|
22
|
-
- 修改代码后**必须**更新 `files` 字段(通过 `sync-files` 或手动)。
|
|
23
|
-
|
|
24
|
-
## Git 合并策略 (Merge Strategy)
|
|
25
|
-
|
|
26
|
-
### 核心原则
|
|
27
|
-
|
|
28
|
-
为确保 Feature 分支安全合并到主线,避免"旧状态污染主线"问题,必须遵循以下合并策略:
|
|
29
|
-
|
|
30
|
-
#### 1. 禁止手动 Merge
|
|
31
|
-
|
|
32
|
-
- **🛑 严禁** Agent 手动执行 `git merge` 合并 Feature 分支
|
|
33
|
-
- **🛑 严禁** 使用 `git pull origin main` 后直接提交
|
|
34
|
-
- **✅ 唯一权威途径**: 必须使用 `monoco issue close` 进行闭环
|
|
35
|
-
|
|
36
|
-
#### 2. 安全合并流程 (Safe Merge Flow)
|
|
37
|
-
|
|
38
|
-
正确的 Issue 关闭流程如下:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
# 1. 确保当前在 main/master 分支,且代码已合并
|
|
42
|
-
$ git checkout main
|
|
43
|
-
$ git pull origin main
|
|
44
|
-
|
|
45
|
-
# 2. 确认 Feature 分支的变更已合并到主线
|
|
46
|
-
# (通过 PR/MR 或其他代码审查流程)
|
|
47
|
-
|
|
48
|
-
# 3. 使用 monoco issue close 关闭 Issue (默认执行 prune)
|
|
49
|
-
$ monoco issue close FEAT-XXXX --solution implemented
|
|
50
|
-
|
|
51
|
-
# 4. 如需保留分支,使用 --no-prune
|
|
52
|
-
$ monoco issue close FEAT-XXXX --solution implemented --no-prune
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
#### 3. 冲突处理原则
|
|
56
|
-
|
|
57
|
-
当 Feature 分支与主线产生冲突时:
|
|
58
|
-
|
|
59
|
-
1. **自动合并停止**: 如果 `touched files` (Issue `files` 字段) 与主线产生冲突,自动化工具**必须立即停止合并**,并抛出明确错误。
|
|
60
|
-
|
|
61
|
-
2. **手动 Cherry-Pick 模式**:
|
|
62
|
-
- 错误信息会指示 Agent 转入手动 Cherry-Pick 模式
|
|
63
|
-
- **核心原则**: 仅挑选属于本 Feature 的有效变更,严禁覆盖主线上无关 Issue 的更新
|
|
64
|
-
- 使用 `git cherry-pick <commit>` 逐个应用有效提交
|
|
65
|
-
|
|
66
|
-
3. **Fallback 策略**:
|
|
67
|
-
```bash
|
|
68
|
-
# 1. 创建临时分支用于解决冲突
|
|
69
|
-
$ git checkout main
|
|
70
|
-
$ git checkout -b temp/FEAT-XXXX-resolve
|
|
71
|
-
|
|
72
|
-
# 2. 逐个 Cherry-Pick 有效提交
|
|
73
|
-
$ git cherry-pick <commit-hash-1>
|
|
74
|
-
$ git cherry-pick <commit-hash-2>
|
|
75
|
-
|
|
76
|
-
# 3. 如有冲突,仅保留本 Feature 的变更
|
|
77
|
-
# 放弃任何会覆盖主线上其他 Issue 更新的修改
|
|
78
|
-
|
|
79
|
-
# 4. 完成后合并临时分支
|
|
80
|
-
$ git checkout main
|
|
81
|
-
$ git merge temp/FEAT-XXXX-resolve
|
|
82
|
-
|
|
83
|
-
# 5. 关闭 Issue
|
|
84
|
-
$ monoco issue close FEAT-XXXX --solution implemented
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
#### 4. 基于 files 字段的智能合并 (Smart Atomic Merge)
|
|
88
|
-
|
|
89
|
-
Issue 的 `files` 字段记录了 Feature 分支的真实影响范围 (Actual Impact Scope):
|
|
90
|
-
|
|
91
|
-
- **生成方式**: `monoco issue sync-files` 使用 `git diff --name-only base...target` 逻辑
|
|
92
|
-
- **作用**: 作为合并白名单,仅合并列表中的文件,过滤因"旧版本基线"导致的隐性覆盖
|
|
93
|
-
- **限制**: 无法防御显式的误操作修改(如无意中格式化其他 Issue 文件)
|
|
94
|
-
|
|
95
|
-
**未来增强**: 基于 `files` 列表实现选择性合并逻辑:
|
|
96
|
-
```bash
|
|
97
|
-
# 选择性合并(规划中)
|
|
98
|
-
$ git checkout main
|
|
99
|
-
$ git checkout feature/FEAT-XXXX -- <files...>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
#### 5. 清理策略
|
|
103
|
-
|
|
104
|
-
- **默认清理**: `monoco issue close` 默认执行 `--prune`,删除 Feature 分支/Worktree
|
|
105
|
-
- **保留分支**: 如需保留分支,显式使用 `--no-prune`
|
|
106
|
-
- **强制清理**: 使用 `--force` 强制删除未完全合并的分支(谨慎使用)
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
# 默认清理分支
|
|
110
|
-
$ monoco issue close FEAT-XXXX --solution implemented
|
|
111
|
-
# ✔ Cleaned up: branch:feat/feat-XXXX-xxx
|
|
112
|
-
|
|
113
|
-
# 保留分支
|
|
114
|
-
$ monoco issue close FEAT-XXXX --solution implemented --no-prune
|
|
9
|
+
- **上下文同步**: `monoco issue sync-files [id]`
|
|
10
|
+
- **结构**: `Issues/{CapitalizedPluralType}/{lowercase_status}/` (如 `Issues/Features/open/`)
|
|
115
11
|
|
|
116
|
-
|
|
117
|
-
$ monoco issue close FEAT-XXXX --solution implemented --force
|
|
118
|
-
```
|
|
12
|
+
## 标准工作流
|
|
119
13
|
|
|
120
|
-
|
|
14
|
+
1. **创建**: `monoco issue create feature -t "标题"`
|
|
15
|
+
2. **启动**: `monoco issue start FEAT-XXX --branch`
|
|
16
|
+
3. **实现**: 正常编码与测试。
|
|
17
|
+
4. **同步**: `monoco issue sync-files` (更新 `files` 字段)。
|
|
18
|
+
5. **提交**: `monoco issue submit FEAT-XXX`。
|
|
19
|
+
6. **合规合并**: `monoco issue close FEAT-XXX --solution implemented` (合并到主线的唯一途径)。
|
|
121
20
|
|
|
122
|
-
|
|
123
|
-
|------|------|------|
|
|
124
|
-
| 创建 Issue | `monoco issue create feature -t "标题"` | 先创建 Issue 再开发 |
|
|
125
|
-
| 启动开发 | `monoco issue start FEAT-XXXX --branch` | 创建 Feature 分支 |
|
|
126
|
-
| 同步文件 | `monoco issue sync-files` | 更新 files 字段 |
|
|
127
|
-
| 提交评审 | `monoco issue submit FEAT-XXXX` | 进入 Review 阶段 |
|
|
128
|
-
| 关闭 Issue | `monoco issue close FEAT-XXXX --solution implemented` | 唯一合并途径 |
|
|
129
|
-
| 保留分支 | `monoco issue close ... --no-prune` | 关闭但不删除分支 |
|
|
21
|
+
## Git 合并策略
|
|
130
22
|
|
|
131
|
-
|
|
23
|
+
- **禁止手动合并**: 严禁在 `main`/`master` 分支执行 `git merge` 或直接 `git pull`。
|
|
24
|
+
- **原子合并**: `monoco issue close` 仅根据 Issue 的 `files` 列表合并变更。
|
|
25
|
+
- **冲突处理**: 若产生冲突,请遵循 `close` 命令产生的指引进行手动 Cherry-Pick。
|
|
26
|
+
- **清理策略**: `monoco issue close` 默认执行清理(删除分支/Worktree)。需保留请指定 `--no-prune`。
|
monoco/features/memo/cli.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Optional
|
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
from rich.table import Table
|
|
6
6
|
from monoco.core.config import get_config
|
|
7
|
-
from .core import add_memo, load_memos, delete_memo,
|
|
7
|
+
from .core import add_memo, load_memos, delete_memo, get_inbox_path, validate_content_language
|
|
8
8
|
|
|
9
9
|
app = typer.Typer(help="Manage memos (fleeting notes).")
|
|
10
10
|
console = Console()
|
|
@@ -70,38 +70,33 @@ def add_command(
|
|
|
70
70
|
|
|
71
71
|
@app.command("list")
|
|
72
72
|
def list_command(
|
|
73
|
-
status: Optional[str] = typer.Option(None, "--status", help="Filter by status (pending, tracked, resolved)."),
|
|
74
73
|
limit: int = typer.Option(None, "--limit", "-n", help="Limit number of memos shown.")
|
|
75
74
|
):
|
|
76
75
|
"""
|
|
77
76
|
List all memos in the inbox.
|
|
77
|
+
|
|
78
|
+
Signal Queue Model: Shows current pending signals.
|
|
79
|
+
Once consumed (file cleared), memos are no longer listed here.
|
|
80
|
+
Use git history to see consumed memos.
|
|
78
81
|
"""
|
|
79
82
|
issues_root = get_issues_root()
|
|
80
83
|
|
|
81
84
|
memos = load_memos(issues_root)
|
|
82
|
-
|
|
83
|
-
if status:
|
|
84
|
-
memos = [m for m in memos if m.status == status]
|
|
85
85
|
|
|
86
86
|
if not memos:
|
|
87
|
-
console.print("No memos
|
|
87
|
+
console.print("No memos in inbox. (Consumed memos are in git history)")
|
|
88
88
|
return
|
|
89
89
|
|
|
90
|
-
# Reverse
|
|
91
|
-
# But file is appended. Let's show newest at bottom (log style) or newest at top?
|
|
92
|
-
# Usually list shows content. Newest at bottom is standard for logs, but for "Inbox" maybe newest top?
|
|
93
|
-
# Let's keep file order (oldest first) unless user asks otherwise, or maybe reverse it for "Inbox" feel?
|
|
94
|
-
# Let's reverse it to see latest first.
|
|
90
|
+
# Reverse to show newest first
|
|
95
91
|
memos.reverse()
|
|
96
92
|
|
|
97
93
|
if limit:
|
|
98
94
|
memos = memos[:limit]
|
|
99
95
|
|
|
100
|
-
table = Table(title="Memo Inbox")
|
|
96
|
+
table = Table(title="Memo Inbox (Pending Signals)")
|
|
101
97
|
table.add_column("ID", style="cyan", no_wrap=True)
|
|
102
|
-
table.add_column("Stat", style="yellow", width=4)
|
|
103
98
|
table.add_column("Type", style="magenta", width=8)
|
|
104
|
-
table.add_column("
|
|
99
|
+
table.add_column("Source", style="blue", width=10)
|
|
105
100
|
table.add_column("Content")
|
|
106
101
|
|
|
107
102
|
for memo in memos:
|
|
@@ -109,21 +104,17 @@ def list_command(
|
|
|
109
104
|
content_preview = memo.content.split("\n")[0]
|
|
110
105
|
if len(content_preview) > 50:
|
|
111
106
|
content_preview = content_preview[:47] + "..."
|
|
112
|
-
|
|
113
|
-
status_icon = " "
|
|
114
|
-
if memo.status == "pending": status_icon = "P"
|
|
115
|
-
elif memo.status == "tracked": status_icon = "T"
|
|
116
|
-
elif memo.status == "resolved": status_icon = "✔"
|
|
117
107
|
|
|
118
108
|
table.add_row(
|
|
119
109
|
memo.uid,
|
|
120
|
-
status_icon,
|
|
121
110
|
memo.type,
|
|
122
|
-
memo.
|
|
111
|
+
memo.source,
|
|
123
112
|
content_preview
|
|
124
113
|
)
|
|
125
114
|
|
|
126
115
|
console.print(table)
|
|
116
|
+
console.print(f"\n[yellow]Note:[/yellow] Memos are consumed (deleted) when processed by Architect.")
|
|
117
|
+
console.print("[dim]Use `git log --follow Memos/inbox.md` to see consumed memos.[/dim]")
|
|
127
118
|
|
|
128
119
|
|
|
129
120
|
@app.command("open")
|
|
@@ -147,6 +138,9 @@ def delete_command(
|
|
|
147
138
|
):
|
|
148
139
|
"""
|
|
149
140
|
Delete a memo from the inbox by its ID.
|
|
141
|
+
|
|
142
|
+
Signal Queue Model: This is a manual delete operation.
|
|
143
|
+
Normally memos are consumed automatically by the MemoThresholdHandler.
|
|
150
144
|
"""
|
|
151
145
|
issues_root = get_issues_root()
|
|
152
146
|
|
|
@@ -157,49 +151,6 @@ def delete_command(
|
|
|
157
151
|
raise typer.Exit(code=1)
|
|
158
152
|
|
|
159
153
|
|
|
160
|
-
@app.command("link")
|
|
161
|
-
def link_command(
|
|
162
|
-
memo_id: str = typer.Argument(..., help="Memo ID"),
|
|
163
|
-
issue_id: str = typer.Argument(..., help="Issue ID to link to")
|
|
164
|
-
):
|
|
165
|
-
"""
|
|
166
|
-
Link a memo to an issue (Traceability).
|
|
167
|
-
Sets status to 'tracked'.
|
|
168
|
-
"""
|
|
169
|
-
issues_root = get_issues_root()
|
|
170
|
-
|
|
171
|
-
updates = {
|
|
172
|
-
"status": "tracked",
|
|
173
|
-
"ref": issue_id
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if update_memo(issues_root, memo_id, updates):
|
|
177
|
-
console.print(f"[green]✔ Memo {memo_id} linked to {issue_id}.[/green]")
|
|
178
|
-
else:
|
|
179
|
-
console.print(f"[red]Error: Memo {memo_id} not found.[/red]")
|
|
180
|
-
raise typer.Exit(code=1)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@app.command("resolve")
|
|
184
|
-
def resolve_command(
|
|
185
|
-
memo_id: str = typer.Argument(..., help="Memo ID")
|
|
186
|
-
):
|
|
187
|
-
"""
|
|
188
|
-
Mark a memo as resolved.
|
|
189
|
-
"""
|
|
190
|
-
issues_root = get_issues_root()
|
|
191
|
-
|
|
192
|
-
updates = {
|
|
193
|
-
"status": "resolved"
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if update_memo(issues_root, memo_id, updates):
|
|
197
|
-
console.print(f"[green]✔ Memo {memo_id} resolved.[/green]")
|
|
198
|
-
else:
|
|
199
|
-
console.print(f"[red]Error: Memo {memo_id} not found.[/red]")
|
|
200
|
-
raise typer.Exit(code=1)
|
|
201
|
-
|
|
202
|
-
|
|
203
154
|
|
|
204
155
|
|
|
205
156
|
|
monoco/features/memo/core.py
CHANGED
|
@@ -39,6 +39,11 @@ def generate_memo_id() -> str:
|
|
|
39
39
|
def parse_memo_block(block: str) -> Optional[Memo]:
|
|
40
40
|
"""
|
|
41
41
|
Parse a text block into a Memo object.
|
|
42
|
+
|
|
43
|
+
Signal Queue Model (FEAT-0165):
|
|
44
|
+
- No status field parsing (file existence is the state)
|
|
45
|
+
- No ref field parsing (traceability via git history)
|
|
46
|
+
|
|
42
47
|
Block format:
|
|
43
48
|
## [uid] YYYY-MM-DD HH:MM:SS
|
|
44
49
|
- **Key**: Value
|
|
@@ -90,17 +95,6 @@ def parse_memo_block(block: str) -> Optional[Memo]:
|
|
|
90
95
|
content_lines.append(line)
|
|
91
96
|
|
|
92
97
|
content = "\n".join(content_lines).strip()
|
|
93
|
-
|
|
94
|
-
# Map metadata to model fields
|
|
95
|
-
# Status map reverse
|
|
96
|
-
status_raw = metadata.get("status", "[ ] Pending")
|
|
97
|
-
status = "pending"
|
|
98
|
-
if "[x] Tracked" in status_raw:
|
|
99
|
-
status = "tracked"
|
|
100
|
-
elif "[x] Resolved" in status_raw:
|
|
101
|
-
status = "resolved"
|
|
102
|
-
elif "[-] Dismissed" in status_raw:
|
|
103
|
-
status = "dismissed"
|
|
104
98
|
|
|
105
99
|
return Memo(
|
|
106
100
|
uid=uid,
|
|
@@ -109,9 +103,7 @@ def parse_memo_block(block: str) -> Optional[Memo]:
|
|
|
109
103
|
author=metadata.get("from", "User"),
|
|
110
104
|
source=metadata.get("source", "cli"),
|
|
111
105
|
type=metadata.get("type", "insight"),
|
|
112
|
-
|
|
113
|
-
ref=metadata.get("ref"),
|
|
114
|
-
context=metadata.get("context") # Note: context might need cleanup if it was wrapped in code blocks
|
|
106
|
+
context=metadata.get("context")
|
|
115
107
|
)
|
|
116
108
|
|
|
117
109
|
def load_memos(issues_root: Path) -> List[Memo]:
|
|
@@ -195,26 +187,6 @@ def add_memo(
|
|
|
195
187
|
|
|
196
188
|
return uid
|
|
197
189
|
|
|
198
|
-
def update_memo(issues_root: Path, memo_id: str, updates: dict) -> bool:
|
|
199
|
-
"""
|
|
200
|
-
Update a memo's fields.
|
|
201
|
-
"""
|
|
202
|
-
memos = load_memos(issues_root)
|
|
203
|
-
found = False
|
|
204
|
-
for i, m in enumerate(memos):
|
|
205
|
-
if m.uid == memo_id:
|
|
206
|
-
# Apply updates
|
|
207
|
-
updated_data = m.model_dump()
|
|
208
|
-
updated_data.update(updates)
|
|
209
|
-
memos[i] = Memo(**updated_data) # Re-validate
|
|
210
|
-
found = True
|
|
211
|
-
break
|
|
212
|
-
|
|
213
|
-
if found:
|
|
214
|
-
save_memos(issues_root, memos)
|
|
215
|
-
|
|
216
|
-
return found
|
|
217
|
-
|
|
218
190
|
def delete_memo(issues_root: Path, memo_id: str) -> bool:
|
|
219
191
|
"""
|
|
220
192
|
Delete a memo by its ID.
|