kortext 1.0.0 → 2.2.0
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.
- package/AGENTS.md +28 -0
- package/README.md +91 -0
- package/agents/backend-developer.md +132 -0
- package/agents/compliance-expert.md +106 -0
- package/agents/copywriter.md +122 -0
- package/agents/db-admin.md +117 -0
- package/agents/delivery-manager.md +121 -0
- package/agents/designer.md +108 -0
- package/agents/devops-engineer.md +141 -0
- package/agents/engineering-manager.md +208 -0
- package/agents/frontend-developer.md +174 -0
- package/agents/growth-expert.md +115 -0
- package/agents/operation-manager.md +122 -0
- package/agents/product-manager.md +114 -0
- package/agents/qa-engineer.md +126 -0
- package/agents/security-engineer.md +119 -0
- package/hooks/audit-logger.sh +25 -0
- package/hooks/auto-locker.sh +74 -0
- package/hooks/auto-unlocker.sh +17 -0
- package/hooks/backlog-sync-guard.sh +12 -0
- package/hooks/branch-guard.sh +32 -0
- package/hooks/commit-msg-guard.sh +43 -0
- package/hooks/git-pre-commit.sh +76 -0
- package/hooks/git-pre-push.sh +17 -0
- package/hooks/handover-guard.sh +33 -0
- package/hooks/kortext-init.sh +194 -0
- package/hooks/kortext-lib.sh +141 -0
- package/hooks/lint-guard.sh +50 -0
- package/hooks/secret-scanner.sh +89 -0
- package/hooks/size-guard.sh +48 -0
- package/hooks/snapshot-guard.sh +39 -0
- package/hooks/write-guard.sh +77 -0
- package/package.json +23 -6
- package/rules/behavior.md +252 -0
- package/rules/branching.md +82 -0
- package/rules/commands.md +112 -0
- package/rules/emergency.md +160 -0
- package/rules/mcp.md +47 -0
- package/rules/models.md +93 -0
- package/scripts/kortext-backlog-add.py +123 -0
- package/scripts/kortext-backlog-health.py +112 -0
- package/scripts/kortext-backlog-sync.py +117 -0
- package/scripts/kortext-bulk-plan.py +63 -0
- package/scripts/kortext-cli.py +295 -0
- package/scripts/kortext-consistency-check.py +74 -0
- package/scripts/kortext-context-check.py +146 -0
- package/scripts/kortext-handover.py +107 -0
- package/scripts/kortext-item-check.py +61 -0
- package/scripts/kortext-item-start.py +70 -0
- package/scripts/kortext-item-transition.py +78 -0
- package/scripts/kortext-lock.py +174 -0
- package/scripts/kortext-session-start.py +127 -0
- package/scripts/lock_kortext.sh +34 -0
- package/settings/.claude-settings.template.json +48 -0
- package/settings/CHANGELOG.md +176 -0
- package/settings/INTEGRATION-MAP.md +276 -0
- package/settings/README.md +255 -0
- package/settings/USER-GUIDE.md +502 -0
- package/settings/VERSION +1 -0
- package/settings/config.md +8 -0
- package/settings/runtime-adapters.md +104 -0
- package/skills/backend-developer/.gitkeep +0 -0
- package/skills/compliance-expert/.gitkeep +0 -0
- package/skills/copywriter/.gitkeep +0 -0
- package/skills/db-admin/.gitkeep +0 -0
- package/skills/delivery-manager/.gitkeep +0 -0
- package/skills/designer/.gitkeep +0 -0
- package/skills/devops-engineer/.gitkeep +0 -0
- package/skills/engineering-manager/.gitkeep +0 -0
- package/skills/frontend-developer/.gitkeep +0 -0
- package/skills/growth-expert/.gitkeep +0 -0
- package/skills/operation-manager/.gitkeep +0 -0
- package/skills/product-manager/.gitkeep +0 -0
- package/skills/qa-engineer/.gitkeep +0 -0
- package/skills/security-engineer/.gitkeep +0 -0
- package/workflows/00-kortext-setup.md +53 -0
- package/workflows/01a-analysis-pipeline.md +117 -0
- package/workflows/01b-onboarding-pipeline.md +110 -0
- package/workflows/02-planning-pipeline.md +146 -0
- package/workflows/02b-spike-workflow.md +120 -0
- package/workflows/03-environment-setup.md +99 -0
- package/workflows/04-development-cycle.md +203 -0
- package/workflows/05-test-cycle.md +123 -0
- package/workflows/06-deployment-cycle.md +224 -0
- package/workflows/07-rollback-pipeline.md +203 -0
- package/workflows/08-hotfix-pipeline.md +196 -0
- package/workflows/09-maintenance-cycle.md +133 -0
- package/workspace/archive/.gitkeep +0 -0
- package/workspace/backups/.gitkeep +0 -0
- package/workspace/memory/backlog/.gitkeep +0 -0
- package/workspace/memory/backlog/BXX-[bug-name].md +2 -0
- package/workspace/memory/backlog/DXX-[debt-name].md +2 -0
- package/workspace/memory/backlog/README.md +31 -0
- package/workspace/memory/backlog/TXX-[task-name].md +2 -0
- package/workspace/memory/backlog/debt-dashboard.md +3 -0
- package/workspace/memory/backlog/epic-dashboard.md +10 -0
- package/workspace/memory/backlog/version-dashboard.md +7 -0
- package/workspace/memory/context/.gitkeep +0 -0
- package/workspace/memory/context/README.md +35 -0
- package/workspace/memory/context/[agent-name]-active.md +2 -0
- package/workspace/memory/decisions.md +17 -0
- package/workspace/memory/handover.md +40 -0
- package/workspace/memory/learned.md +21 -0
- package/workspace/references/access.md +47 -0
- package/workspace/references/api-reference.md +59 -0
- package/workspace/references/blueprint.md +29 -0
- package/workspace/references/content-strategy.md +39 -0
- package/workspace/references/db-schema.md +51 -0
- package/workspace/references/design-system.md +107 -0
- package/workspace/references/dictionary.md +42 -0
- package/workspace/references/file-system.md +42 -0
- package/workspace/references/growth-strategy.md +46 -0
- package/workspace/references/legal-strategy.md +35 -0
- package/workspace/references/required-skills.md +11 -0
- package/workspace/references/security-rules.md +34 -0
- package/workspace/references/tech-stack.md +49 -0
- package/workspace/references/test-strategy.md +48 -0
- package/workspace/reports/analysis-reports.md +40 -0
- package/workspace/reports/content-reports.md +29 -0
- package/workspace/reports/delivery-reports.md +26 -0
- package/workspace/reports/growth-reports.md +9 -0
- package/workspace/reports/legal-reports.md +29 -0
- package/workspace/reports/product-requirements.md +28 -0
- package/workspace/reports/release-notes.md +20 -0
- package/workspace/reports/security-reports.md +27 -0
- package/workspace/reports/status-reports.md +48 -0
- package/workspace/reports/tech-requirements.md +30 -0
- package/workspace/reports/test-reports.md +61 -0
- package/workspace/templates/BXX-[bug-name].md +34 -0
- package/workspace/templates/DXX-[debt-name].md +35 -0
- package/workspace/templates/EXX-[epic-name].md +48 -0
- package/workspace/templates/HXX-[hotfix-name].md +73 -0
- package/workspace/templates/SXX-[spike-name].md +51 -0
- package/workspace/templates/TXX-[task-name].md +34 -0
- package/workspace/templates/[agent-name]-active.md +4 -0
- package/workspace/templates/[original-name]_[YYYY-MM-DD_HHMMSS].md +5 -0
- package/workspace/templates/adr-XXX.md +66 -0
- package/workspace/templates/debt-dashboard.md +10 -0
- package/workspace/templates/epic-dashboard.md +10 -0
- package/workspace/templates/version-dashboard.md +10 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
BACKLOG_ROOT = Path("workspace/memory/backlog")
|
|
13
|
+
CONTEXT_ROOT = Path("workspace/memory/context")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def update_status(content: str, status: str) -> str:
|
|
17
|
+
return re.sub(r"> \*\*Status:\*\* .*", f"> **Status:** {status}", content, count=1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _atomic_commit(message: str, paths: list[str]) -> None:
|
|
21
|
+
# Kortext atomic operation: dosyaları add edip commit'ler.
|
|
22
|
+
# Git repo değilse, değişiklik yoksa veya hook reddederse sessizce geç.
|
|
23
|
+
if not shutil.which("git"):
|
|
24
|
+
return
|
|
25
|
+
try:
|
|
26
|
+
subprocess.run(["git", "add", "--", *paths], check=True, capture_output=True)
|
|
27
|
+
subprocess.run(
|
|
28
|
+
["git", "commit", "-m", message, "--", *paths],
|
|
29
|
+
check=True,
|
|
30
|
+
capture_output=True,
|
|
31
|
+
)
|
|
32
|
+
except subprocess.CalledProcessError:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main() -> None:
|
|
37
|
+
parser = argparse.ArgumentParser(description="Start a Kortext backlog item")
|
|
38
|
+
parser.add_argument("item_path")
|
|
39
|
+
parser.add_argument("--agent", required=True, help="e.g. backend-developer")
|
|
40
|
+
parser.add_argument("--summary", required=True)
|
|
41
|
+
parser.add_argument("--no-commit", action="store_true", help="Atomic git commit'i atla")
|
|
42
|
+
args = parser.parse_args()
|
|
43
|
+
|
|
44
|
+
item_path = Path(args.item_path)
|
|
45
|
+
if not item_path.exists():
|
|
46
|
+
raise SystemExit(f"Hata: Dosya bulunamadı: {item_path}")
|
|
47
|
+
|
|
48
|
+
item_id = item_path.stem.split("-", 1)[0]
|
|
49
|
+
content = item_path.read_text()
|
|
50
|
+
item_path.write_text(update_status(content, "In Progress"))
|
|
51
|
+
|
|
52
|
+
CONTEXT_ROOT.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
timestamp = datetime.now().strftime("%H:%M")
|
|
54
|
+
context_path = CONTEXT_ROOT / f"{args.agent}-active.md"
|
|
55
|
+
context_path.write_text(
|
|
56
|
+
f"### +{args.agent} | {item_id} | In Progress | {timestamp} | {args.summary}\n"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
print(f"✅ Başlatıldı: {item_id}")
|
|
60
|
+
print(f"✅ Context güncellendi: {context_path}")
|
|
61
|
+
|
|
62
|
+
if not args.no_commit:
|
|
63
|
+
_atomic_commit(
|
|
64
|
+
f"chore(kortext): start {item_id} [skip ci]",
|
|
65
|
+
[str(item_path), str(context_path)],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import re
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ALLOWED_TRANSITIONS = {
|
|
12
|
+
"To Do": {"In Progress"},
|
|
13
|
+
"In Progress": {"Test"},
|
|
14
|
+
"Test": {"In Progress", "Review"},
|
|
15
|
+
"Review": {"In Progress", "Done"},
|
|
16
|
+
"Done": set(),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def read_status(content: str) -> str:
|
|
21
|
+
match = re.search(r"> \*\*Status:\*\* (.+)", content)
|
|
22
|
+
if not match:
|
|
23
|
+
raise SystemExit("Hata: Item dosyasında status alanı bulunamadı.")
|
|
24
|
+
return match.group(1).strip()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def write_status(content: str, status: str) -> str:
|
|
28
|
+
return re.sub(r"> \*\*Status:\*\* .*", f"> **Status:** {status}", content, count=1)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _atomic_commit(message: str, paths: list[str]) -> None:
|
|
32
|
+
# Kortext atomic operation: dosyaları add edip commit'ler.
|
|
33
|
+
if not shutil.which("git"):
|
|
34
|
+
return
|
|
35
|
+
try:
|
|
36
|
+
subprocess.run(["git", "add", "--", *paths], check=True, capture_output=True)
|
|
37
|
+
subprocess.run(
|
|
38
|
+
["git", "commit", "-m", message, "--", *paths],
|
|
39
|
+
check=True,
|
|
40
|
+
capture_output=True,
|
|
41
|
+
)
|
|
42
|
+
except subprocess.CalledProcessError:
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def main() -> None:
|
|
47
|
+
parser = argparse.ArgumentParser(description="Transition a Kortext item status")
|
|
48
|
+
parser.add_argument("item_path")
|
|
49
|
+
parser.add_argument("--to", required=True, choices=sorted(ALLOWED_TRANSITIONS))
|
|
50
|
+
parser.add_argument("--no-commit", action="store_true", help="Atomic git commit'i atla")
|
|
51
|
+
args = parser.parse_args()
|
|
52
|
+
|
|
53
|
+
item_path = Path(args.item_path)
|
|
54
|
+
if not item_path.exists():
|
|
55
|
+
raise SystemExit(f"Hata: Dosya bulunamadı: {item_path}")
|
|
56
|
+
|
|
57
|
+
content = item_path.read_text()
|
|
58
|
+
current = read_status(content)
|
|
59
|
+
allowed = ALLOWED_TRANSITIONS.get(current)
|
|
60
|
+
if allowed is None:
|
|
61
|
+
raise SystemExit(f"Hata: Geçersiz mevcut status: {current}")
|
|
62
|
+
if args.to not in allowed:
|
|
63
|
+
raise SystemExit(f"Hata: Geçersiz geçiş: {current} -> {args.to}")
|
|
64
|
+
|
|
65
|
+
item_path.write_text(write_status(content, args.to))
|
|
66
|
+
print(f"✅ Geçiş yapıldı: {current} -> {args.to}")
|
|
67
|
+
|
|
68
|
+
if not args.no_commit:
|
|
69
|
+
item_id = item_path.stem.split("-", 1)[0]
|
|
70
|
+
slug = args.to.lower().replace(" ", "-")
|
|
71
|
+
_atomic_commit(
|
|
72
|
+
f"chore(kortext): transition {item_id} -> {slug} [skip ci]",
|
|
73
|
+
[str(item_path)],
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
main()
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Kortext File Lock
|
|
4
|
+
-----------------
|
|
5
|
+
Paylaşımlı dosyalara (handover.md, epic-dashboard.md vb.) eş zamanlı yazma
|
|
6
|
+
çakışmasını önlemek için dosya kilidi mekanizması sağlar.
|
|
7
|
+
|
|
8
|
+
Kullanım:
|
|
9
|
+
# Kilitlemek için:
|
|
10
|
+
python kortext-lock.py acquire --file workspace/memory/handover.md --agent backend-developer
|
|
11
|
+
|
|
12
|
+
# Kilidi serbest bırakmak için:
|
|
13
|
+
python kortext-lock.py release --file workspace/memory/handover.md
|
|
14
|
+
|
|
15
|
+
# Kilit durumunu kontrol etmek için:
|
|
16
|
+
python kortext-lock.py status --file workspace/memory/handover.md
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from datetime import datetime, timedelta
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
28
|
+
LOCK_DIR = ROOT / "workspace/.locks"
|
|
29
|
+
|
|
30
|
+
# Saniye cinsinden lock timeout. Env var ile override edilebilir.
|
|
31
|
+
# Default: 300 saniye = 5 dakika.
|
|
32
|
+
LOCK_TIMEOUT_SECONDS = int(os.environ.get("KORTEXT_LOCK_TIMEOUT_SECONDS", "300"))
|
|
33
|
+
LOCK_TIMEOUT_MINUTES = LOCK_TIMEOUT_SECONDS / 60
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def lock_path(target_file: str) -> Path:
|
|
37
|
+
safe_name = target_file.replace("/", "_").replace("\\", "_")
|
|
38
|
+
return LOCK_DIR / f"{safe_name}.lock"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def acquire(target_file: str, agent: str) -> None:
|
|
42
|
+
LOCK_DIR.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
lp = lock_path(target_file)
|
|
44
|
+
|
|
45
|
+
# Stale lock kontrolü — saniye bazında karşılaştırılır.
|
|
46
|
+
if lp.exists():
|
|
47
|
+
content = lp.read_text(errors="ignore")
|
|
48
|
+
lines = content.splitlines()
|
|
49
|
+
# Zaman damgasını parse et
|
|
50
|
+
time_line = next((l for l in lines if l.startswith("time:")), None)
|
|
51
|
+
if time_line:
|
|
52
|
+
try:
|
|
53
|
+
lock_time = datetime.fromisoformat(time_line.split(":", 1)[1].strip())
|
|
54
|
+
age = datetime.now() - lock_time
|
|
55
|
+
if age > timedelta(seconds=LOCK_TIMEOUT_SECONDS):
|
|
56
|
+
print(
|
|
57
|
+
f"⚠️ Stale kilit tespit edildi ({round(age.total_seconds()/60, 1)} dakika). "
|
|
58
|
+
f"Otomatik kaldırılıyor..."
|
|
59
|
+
)
|
|
60
|
+
lp.unlink()
|
|
61
|
+
else:
|
|
62
|
+
owner_line = next((l for l in lines if l.startswith("agent:")), "agent: bilinmiyor")
|
|
63
|
+
owner = owner_line.split(":", 1)[1].strip()
|
|
64
|
+
# Türkçe hata mesajı stderr'e — fail durumunda exit 1.
|
|
65
|
+
print(
|
|
66
|
+
f"🔒 Kilit alınamadı: {target_file} — sahibi: +{owner}, "
|
|
67
|
+
f"son güncelleme: {lock_time.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
68
|
+
file=sys.stderr,
|
|
69
|
+
)
|
|
70
|
+
print(
|
|
71
|
+
f" Otomatik açılır: {round(LOCK_TIMEOUT_MINUTES, 1)} dakika sonra",
|
|
72
|
+
file=sys.stderr,
|
|
73
|
+
)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
except (ValueError, IndexError):
|
|
76
|
+
# Parse edilemezse stale say
|
|
77
|
+
lp.unlink()
|
|
78
|
+
|
|
79
|
+
# Kilidi al
|
|
80
|
+
now = datetime.now()
|
|
81
|
+
lp.write_text(
|
|
82
|
+
f"agent: {agent}\n"
|
|
83
|
+
f"file: {target_file}\n"
|
|
84
|
+
f"time: {now.isoformat()}\n"
|
|
85
|
+
)
|
|
86
|
+
print(f"✅ Kilit alındı: {target_file} (+{agent})")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def release(target_file: str) -> None:
|
|
90
|
+
lp = lock_path(target_file)
|
|
91
|
+
if lp.exists():
|
|
92
|
+
lp.unlink()
|
|
93
|
+
print(f"✅ Kilit kaldırıldı: {target_file}")
|
|
94
|
+
else:
|
|
95
|
+
print(f"ℹ️ Kilit zaten yok: {target_file}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def status(target_file: str) -> None:
|
|
99
|
+
lp = lock_path(target_file)
|
|
100
|
+
if not lp.exists():
|
|
101
|
+
print(f"🔓 Serbest: {target_file}")
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
content = lp.read_text(errors="ignore")
|
|
105
|
+
lines = content.splitlines()
|
|
106
|
+
agent_line = next((l for l in lines if l.startswith("agent:")), "agent: bilinmiyor")
|
|
107
|
+
time_line = next((l for l in lines if l.startswith("time:")), None)
|
|
108
|
+
agent = agent_line.split(":", 1)[1].strip()
|
|
109
|
+
if time_line:
|
|
110
|
+
try:
|
|
111
|
+
lock_time = datetime.fromisoformat(time_line.split(":", 1)[1].strip())
|
|
112
|
+
age = datetime.now() - lock_time
|
|
113
|
+
stale = age > timedelta(seconds=LOCK_TIMEOUT_SECONDS)
|
|
114
|
+
stale_flag = " ⚠️ STALE" if stale else ""
|
|
115
|
+
print(f"🔒 Kilitli: {target_file}")
|
|
116
|
+
print(f" Sahibi: +{agent}")
|
|
117
|
+
print(f" Süre : {round(age.total_seconds()/60, 1)} dakika{stale_flag}")
|
|
118
|
+
except (ValueError, IndexError):
|
|
119
|
+
print(f"🔒 Kilitli (parse hatası): {target_file}")
|
|
120
|
+
else:
|
|
121
|
+
print(f"🔒 Kilitli: {target_file} (+{agent})")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def list_all() -> None:
|
|
125
|
+
if not LOCK_DIR.exists():
|
|
126
|
+
print("ℹ️ Aktif kilit yok.")
|
|
127
|
+
return
|
|
128
|
+
locks = list(LOCK_DIR.glob("*.lock"))
|
|
129
|
+
if not locks:
|
|
130
|
+
print("ℹ️ Aktif kilit yok.")
|
|
131
|
+
return
|
|
132
|
+
print(f"🔒 Aktif kilitler ({len(locks)} adet):")
|
|
133
|
+
for lp in sorted(locks):
|
|
134
|
+
content = lp.read_text(errors="ignore")
|
|
135
|
+
lines = content.splitlines()
|
|
136
|
+
agent_line = next((l for l in lines if l.startswith("agent:")), "agent: ?")
|
|
137
|
+
file_line = next((l for l in lines if l.startswith("file:")), "file: ?")
|
|
138
|
+
agent = agent_line.split(":", 1)[1].strip()
|
|
139
|
+
file_ref = file_line.split(":", 1)[1].strip()
|
|
140
|
+
print(f" • {file_ref} → +{agent}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def main() -> None:
|
|
144
|
+
parser = argparse.ArgumentParser(description="Kortext File Lock Manager")
|
|
145
|
+
sub = parser.add_subparsers(dest="command")
|
|
146
|
+
|
|
147
|
+
acq = sub.add_parser("acquire", help="Dosya kilidi al")
|
|
148
|
+
acq.add_argument("--file", required=True, help="Kilitlenecek dosya yolu")
|
|
149
|
+
acq.add_argument("--agent", required=True, help="Kilidi alan ajan (örn: backend-developer)")
|
|
150
|
+
|
|
151
|
+
rel = sub.add_parser("release", help="Dosya kilidini kaldır")
|
|
152
|
+
rel.add_argument("--file", required=True, help="Kilidi kaldırılacak dosya yolu")
|
|
153
|
+
|
|
154
|
+
stat = sub.add_parser("status", help="Dosya kilit durumu")
|
|
155
|
+
stat.add_argument("--file", required=True, help="Kontrol edilecek dosya yolu")
|
|
156
|
+
|
|
157
|
+
sub.add_parser("list", help="Tüm aktif kilitleri listele")
|
|
158
|
+
|
|
159
|
+
args = parser.parse_args()
|
|
160
|
+
|
|
161
|
+
if args.command == "acquire":
|
|
162
|
+
acquire(args.file, args.agent)
|
|
163
|
+
elif args.command == "release":
|
|
164
|
+
release(args.file)
|
|
165
|
+
elif args.command == "status":
|
|
166
|
+
status(args.file)
|
|
167
|
+
elif args.command == "list":
|
|
168
|
+
list_all()
|
|
169
|
+
else:
|
|
170
|
+
parser.print_help()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
main()
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Kortext Session Start
|
|
4
|
+
---------------------
|
|
5
|
+
Her oturum açılışında çalışır. Context dosyalarını okur, handover'ı kontrol eder
|
|
6
|
+
ve SESSION_BRIEF üretir. Ajan bu özeti görmeden göreve başlamamalıdır.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime, timedelta
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
16
|
+
CONTEXT_DIR = ROOT / "workspace/memory/context"
|
|
17
|
+
HANDOVER_PATH = ROOT / "workspace/memory/handover.md"
|
|
18
|
+
BACKLOG_DIR = ROOT / "workspace/memory/backlog"
|
|
19
|
+
|
|
20
|
+
# Stale eşiği saat cinsinden. Env var ile override edilebilir.
|
|
21
|
+
STALE_HOURS = int(os.environ.get("KORTEXT_STALE_HOURS_CONTEXT", "24"))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def read_context_files() -> list[dict]:
|
|
25
|
+
"""Tüm aktif context dosyalarını okur."""
|
|
26
|
+
agents = []
|
|
27
|
+
if not CONTEXT_DIR.exists():
|
|
28
|
+
return agents
|
|
29
|
+
for f in sorted(CONTEXT_DIR.glob("*-active.md")):
|
|
30
|
+
if f.name.startswith("["): # şablon dosyalarını atla
|
|
31
|
+
continue
|
|
32
|
+
content = f.read_text(errors="ignore").strip()
|
|
33
|
+
mtime = datetime.fromtimestamp(f.stat().st_mtime)
|
|
34
|
+
age_hours = (datetime.now() - mtime).total_seconds() / 3600
|
|
35
|
+
agents.append({
|
|
36
|
+
"file": f.name,
|
|
37
|
+
"content": content,
|
|
38
|
+
"age_hours": round(age_hours, 1),
|
|
39
|
+
"stale": age_hours > STALE_HOURS,
|
|
40
|
+
})
|
|
41
|
+
return agents
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_last_handover() -> str:
|
|
45
|
+
"""handover.md dosyasından en son handover bloğunu döndürür."""
|
|
46
|
+
if not HANDOVER_PATH.exists():
|
|
47
|
+
return "(handover.md bulunamadı)"
|
|
48
|
+
text = HANDOVER_PATH.read_text(errors="ignore")
|
|
49
|
+
# İlk ## Handover: bloğunu bul
|
|
50
|
+
lines = text.splitlines()
|
|
51
|
+
block: list[str] = []
|
|
52
|
+
in_block = False
|
|
53
|
+
for line in lines:
|
|
54
|
+
if line.startswith("## Handover:"):
|
|
55
|
+
if in_block:
|
|
56
|
+
break # Sadece ilk (en yeni) bloğu al
|
|
57
|
+
in_block = True
|
|
58
|
+
if in_block:
|
|
59
|
+
block.append(line)
|
|
60
|
+
return "\n".join(block) if block else "(kayıtlı handover yok)"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def count_in_progress_items() -> list[str]:
|
|
64
|
+
"""Backlog'da In Progress olan itemları döndürür."""
|
|
65
|
+
items = []
|
|
66
|
+
if not BACKLOG_DIR.exists():
|
|
67
|
+
return items
|
|
68
|
+
for f in BACKLOG_DIR.glob("*.md"):
|
|
69
|
+
text = f.read_text(errors="ignore")
|
|
70
|
+
if "**Status:** In Progress" in text:
|
|
71
|
+
items.append(f.stem)
|
|
72
|
+
return items
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main() -> None:
|
|
76
|
+
now = datetime.now().strftime("%d.%m.%Y %H:%M")
|
|
77
|
+
print("=" * 60)
|
|
78
|
+
print(f" KORTEXT SESSION BRIEF — {now}")
|
|
79
|
+
print("=" * 60)
|
|
80
|
+
|
|
81
|
+
# 1. Aktif ajanlar
|
|
82
|
+
agents = read_context_files()
|
|
83
|
+
print(f"\n📋 AKTİF AJANLAR ({len(agents)} dosya):")
|
|
84
|
+
if not agents:
|
|
85
|
+
print(" → Context dosyası yok. Temiz başlangıç.")
|
|
86
|
+
for agent in agents:
|
|
87
|
+
stale_flag = " ⚠️ STALE" if agent["stale"] else ""
|
|
88
|
+
print(f" • {agent['file']} ({agent['age_hours']}s önce güncellendi{stale_flag})")
|
|
89
|
+
# İlk satırı özetle
|
|
90
|
+
first_line = agent["content"].splitlines()[0] if agent["content"] else ""
|
|
91
|
+
if first_line:
|
|
92
|
+
print(f" {first_line}")
|
|
93
|
+
|
|
94
|
+
# 2. In Progress itemlar
|
|
95
|
+
in_progress = count_in_progress_items()
|
|
96
|
+
print(f"\n🔄 IN PROGRESS İTEMLAR ({len(in_progress)} adet):")
|
|
97
|
+
if not in_progress:
|
|
98
|
+
print(" → Yok.")
|
|
99
|
+
for item in in_progress:
|
|
100
|
+
print(f" • {item}")
|
|
101
|
+
|
|
102
|
+
# 3. Son handover
|
|
103
|
+
print(f"\n📨 SON HANDOVER:")
|
|
104
|
+
handover = read_last_handover()
|
|
105
|
+
# İlk 10 satırı göster
|
|
106
|
+
preview_lines = handover.splitlines()[:10]
|
|
107
|
+
for line in preview_lines:
|
|
108
|
+
print(f" {line}")
|
|
109
|
+
if len(handover.splitlines()) > 10:
|
|
110
|
+
print(f" ... ({len(handover.splitlines())} satır — tam okuma için handover.md)")
|
|
111
|
+
|
|
112
|
+
# 4. Stale uyarıları
|
|
113
|
+
stale_agents = [a for a in agents if a["stale"]]
|
|
114
|
+
if stale_agents:
|
|
115
|
+
print(f"\n⚠️ STALE CONTEXT UYARISI ({len(stale_agents)} dosya):")
|
|
116
|
+
for a in stale_agents:
|
|
117
|
+
print(f" • {a['file']} — {a['age_hours']} saat önce güncellendi")
|
|
118
|
+
print(" → Bu dosyalar hala In Progress görünüyor. Kontrol et.")
|
|
119
|
+
|
|
120
|
+
print("\n" + "=" * 60)
|
|
121
|
+
print(" Context yüklendi. Göreve başlamadan önce yukarıdaki")
|
|
122
|
+
print(" bilgileri oku ve durumu anla.")
|
|
123
|
+
print("=" * 60)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
main()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
KORTEXT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
5
|
+
KORTEXT_ROOT="$(cd "$KORTEXT_DIR/.." && pwd)"
|
|
6
|
+
|
|
7
|
+
# Bu script Kortext Framework'ün kritik dosyalarını sadece okunabilir hale getirir.
|
|
8
|
+
|
|
9
|
+
# Kök dizin dosyaları
|
|
10
|
+
FILES=("$KORTEXT_DIR/AGENTS.md")
|
|
11
|
+
|
|
12
|
+
# Korumaya alınacak Framework klasörleri
|
|
13
|
+
DIRS="$KORTEXT_DIR/agents $KORTEXT_DIR/rules $KORTEXT_DIR/workflows"
|
|
14
|
+
|
|
15
|
+
echo "Kortext Framework dosyaları kilitleniyor..."
|
|
16
|
+
|
|
17
|
+
# Dosyaları kilitle
|
|
18
|
+
for FILE in "${FILES[@]}"; do
|
|
19
|
+
if [ -f "$FILE" ]; then
|
|
20
|
+
echo "- $FILE kilitleniyor..."
|
|
21
|
+
chmod 444 "$FILE"
|
|
22
|
+
fi
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
# Klasörleri ve içindekileri kilitle (Yazma yetkisini kaldır, okuma ve erişim yetkisini koru)
|
|
26
|
+
for DIR in $DIRS; do
|
|
27
|
+
if [ -d "$DIR" ]; then
|
|
28
|
+
echo "- $DIR klasörü ve içeriği kilitleniyor..."
|
|
29
|
+
chmod -R a-w "$DIR"
|
|
30
|
+
chmod -R a+rX "$DIR"
|
|
31
|
+
fi
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
echo "İşlem tamamlandı. Framework dosyaları artık sadece okunabilir."
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Write|Edit",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/write-guard.sh" },
|
|
8
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/secret-scanner.sh" },
|
|
9
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/auto-locker.sh" },
|
|
10
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/snapshot-guard.sh" }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "Bash",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/secret-scanner.sh" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"PostToolUse": [
|
|
21
|
+
{
|
|
22
|
+
"matcher": "Write|Edit",
|
|
23
|
+
"hooks": [
|
|
24
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/auto-unlocker.sh" },
|
|
25
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/audit-logger.sh" }
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"matcher": "*",
|
|
30
|
+
"hooks": [
|
|
31
|
+
{ "type": "command", "command": "${KORTEXT_ROOT}/hooks/audit-logger.sh" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"SessionStart": [
|
|
36
|
+
{
|
|
37
|
+
"hooks": [
|
|
38
|
+
{ "type": "command", "command": "python3 ${KORTEXT_ROOT}/scripts/kortext-session-start.py" }
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"env": {
|
|
44
|
+
"KORTEXT_HOOK_MODE": "permissive",
|
|
45
|
+
"KORTEXT_INTERACTION_LANGUAGE": "tr",
|
|
46
|
+
"KORTEXT_RUNTIME": "claude_code"
|
|
47
|
+
}
|
|
48
|
+
}
|