kortext 2.2.0 → 3.0.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 +7 -11
- package/CHANGELOG.md +81 -0
- package/LICENSE +21 -0
- package/README.md +144 -47
- package/USER-GUIDE.md +505 -0
- package/bin/kortext.js +23 -0
- package/bin/kortext.ts +377 -0
- package/dist/bin/kortext.js +346 -0
- package/dist/bin/kortext.js.map +1 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.js +553 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/sse.js +38 -0
- package/dist/mcp/sse.js.map +1 -0
- package/dist/mcp/stdio.js +44 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/server/cli/cleanup.js +73 -0
- package/dist/server/cli/cleanup.js.map +1 -0
- package/dist/server/cli/commands.js +73 -0
- package/dist/server/cli/commands.js.map +1 -0
- package/dist/server/cli/doctor.js +118 -0
- package/dist/server/cli/doctor.js.map +1 -0
- package/dist/server/cli/executor-factory.js +39 -0
- package/dist/server/cli/executor-factory.js.map +1 -0
- package/dist/server/cli/init.js +121 -0
- package/dist/server/cli/init.js.map +1 -0
- package/dist/server/cli/logs.js +28 -0
- package/dist/server/cli/logs.js.map +1 -0
- package/dist/server/cli/serve.js +72 -0
- package/dist/server/cli/serve.js.map +1 -0
- package/dist/server/config/env.js +12 -0
- package/dist/server/config/env.js.map +1 -0
- package/dist/server/db/client.js +32 -0
- package/dist/server/db/client.js.map +1 -0
- package/dist/server/db/json.js +21 -0
- package/dist/server/db/json.js.map +1 -0
- package/dist/server/db/migrate.js +70 -0
- package/dist/server/db/migrate.js.map +1 -0
- package/dist/server/db/migrations/001_init.sql +243 -0
- package/dist/server/db/repositories/audit-log.js +58 -0
- package/dist/server/db/repositories/audit-log.js.map +1 -0
- package/dist/server/db/repositories/backlog.js +96 -0
- package/dist/server/db/repositories/backlog.js.map +1 -0
- package/dist/server/db/repositories/contexts.js +54 -0
- package/dist/server/db/repositories/contexts.js.map +1 -0
- package/dist/server/db/repositories/decisions.js +71 -0
- package/dist/server/db/repositories/decisions.js.map +1 -0
- package/dist/server/db/repositories/handovers.js +53 -0
- package/dist/server/db/repositories/handovers.js.map +1 -0
- package/dist/server/db/repositories/index.js +30 -0
- package/dist/server/db/repositories/index.js.map +1 -0
- package/dist/server/db/repositories/locks.js +59 -0
- package/dist/server/db/repositories/locks.js.map +1 -0
- package/dist/server/db/repositories/notifications.js +63 -0
- package/dist/server/db/repositories/notifications.js.map +1 -0
- package/dist/server/db/repositories/pending-questions.js +63 -0
- package/dist/server/db/repositories/pending-questions.js.map +1 -0
- package/dist/server/db/repositories/runs.js +138 -0
- package/dist/server/db/repositories/runs.js.map +1 -0
- package/dist/server/db/repositories/runtime-artifacts.js +39 -0
- package/dist/server/db/repositories/runtime-artifacts.js.map +1 -0
- package/dist/server/db/repositories/secrets.js +65 -0
- package/dist/server/db/repositories/secrets.js.map +1 -0
- package/dist/server/db/repositories/sessions.js +48 -0
- package/dist/server/db/repositories/sessions.js.map +1 -0
- package/dist/server/db/schemas.js +308 -0
- package/dist/server/db/schemas.js.map +1 -0
- package/dist/server/engine/consistency.js +25 -0
- package/dist/server/engine/consistency.js.map +1 -0
- package/dist/server/engine/dag.js +86 -0
- package/dist/server/engine/dag.js.map +1 -0
- package/dist/server/engine/executor.js +2 -0
- package/dist/server/engine/executor.js.map +1 -0
- package/dist/server/engine/executors/claude-cli-executor.js +83 -0
- package/dist/server/engine/executors/claude-cli-executor.js.map +1 -0
- package/dist/server/engine/executors/cli-spawn.js +127 -0
- package/dist/server/engine/executors/cli-spawn.js.map +1 -0
- package/dist/server/engine/executors/codex-cli-executor.js +69 -0
- package/dist/server/engine/executors/codex-cli-executor.js.map +1 -0
- package/dist/server/engine/executors/gemini-cli-executor.js +69 -0
- package/dist/server/engine/executors/gemini-cli-executor.js.map +1 -0
- package/dist/server/engine/executors/mock-executor.js +52 -0
- package/dist/server/engine/executors/mock-executor.js.map +1 -0
- package/dist/server/engine/executors/persona-routed-executor.js +17 -0
- package/dist/server/engine/executors/persona-routed-executor.js.map +1 -0
- package/dist/server/engine/gate-enforcer.js +75 -0
- package/dist/server/engine/gate-enforcer.js.map +1 -0
- package/dist/server/engine/git-commit.js +42 -0
- package/dist/server/engine/git-commit.js.map +1 -0
- package/dist/server/engine/handover.js +120 -0
- package/dist/server/engine/handover.js.map +1 -0
- package/dist/server/engine/item-lifecycle.js +74 -0
- package/dist/server/engine/item-lifecycle.js.map +1 -0
- package/dist/server/engine/persona-registry.js +108 -0
- package/dist/server/engine/persona-registry.js.map +1 -0
- package/dist/server/engine/worker-pool.js +324 -0
- package/dist/server/engine/worker-pool.js.map +1 -0
- package/dist/server/engine/workflow-loader.js +55 -0
- package/dist/server/engine/workflow-loader.js.map +1 -0
- package/dist/server/engine/workflow-parser.js +158 -0
- package/dist/server/engine/workflow-parser.js.map +1 -0
- package/dist/server/engine/worktree.js +176 -0
- package/dist/server/engine/worktree.js.map +1 -0
- package/dist/server/index.js +99 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/notifications/dispatcher.js +60 -0
- package/dist/server/notifications/dispatcher.js.map +1 -0
- package/dist/server/notifications/slack.js +37 -0
- package/dist/server/notifications/slack.js.map +1 -0
- package/dist/server/notifications/telegram.js +33 -0
- package/dist/server/notifications/telegram.js.map +1 -0
- package/dist/server/orchestrator/approval-queue.js +83 -0
- package/dist/server/orchestrator/approval-queue.js.map +1 -0
- package/dist/server/orchestrator/blueprint-watcher.js +94 -0
- package/dist/server/orchestrator/blueprint-watcher.js.map +1 -0
- package/dist/server/orchestrator/orchestrator.js +283 -0
- package/dist/server/orchestrator/orchestrator.js.map +1 -0
- package/dist/server/orchestrator/pipeline-chainer.js +96 -0
- package/dist/server/orchestrator/pipeline-chainer.js.map +1 -0
- package/dist/server/orchestrator/resume.js +36 -0
- package/dist/server/orchestrator/resume.js.map +1 -0
- package/dist/server/routes/approvals.js +52 -0
- package/dist/server/routes/approvals.js.map +1 -0
- package/dist/server/routes/backlog.js +44 -0
- package/dist/server/routes/backlog.js.map +1 -0
- package/dist/server/routes/db-info.js +21 -0
- package/dist/server/routes/db-info.js.map +1 -0
- package/dist/server/routes/docs.js +70 -0
- package/dist/server/routes/docs.js.map +1 -0
- package/dist/server/routes/doctor.js +19 -0
- package/dist/server/routes/doctor.js.map +1 -0
- package/dist/server/routes/handovers.js +25 -0
- package/dist/server/routes/handovers.js.map +1 -0
- package/dist/server/routes/health.js +11 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/personas.js +96 -0
- package/dist/server/routes/personas.js.map +1 -0
- package/dist/server/routes/runs.js +41 -0
- package/dist/server/routes/runs.js.map +1 -0
- package/dist/server/routes/workflows.js +38 -0
- package/dist/server/routes/workflows.js.map +1 -0
- package/dist/server/safety/harmful-output-filter.js +40 -0
- package/dist/server/safety/harmful-output-filter.js.map +1 -0
- package/dist/server/safety/secret-scanner.js +169 -0
- package/dist/server/safety/secret-scanner.js.map +1 -0
- package/dist/server/services/markdown-sync.js +101 -0
- package/dist/server/services/markdown-sync.js.map +1 -0
- package/dist/web/assets/index-B80d5ZPZ.js +75 -0
- package/dist/web/assets/index-B80d5ZPZ.js.map +1 -0
- package/dist/web/assets/index-Bge5Gzqv.css +1 -0
- package/dist/web/index.html +13 -0
- package/docs/architecture.md +413 -0
- package/package.json +83 -11
- package/rules/behavior.md +2 -2
- package/scripts/copy-migrations.mjs +29 -0
- package/workspace/.locks/workspace_handover.md.lock +3 -0
- package/hooks/audit-logger.sh +0 -25
- package/hooks/auto-locker.sh +0 -74
- package/hooks/auto-unlocker.sh +0 -17
- package/hooks/backlog-sync-guard.sh +0 -12
- package/hooks/branch-guard.sh +0 -32
- package/hooks/commit-msg-guard.sh +0 -43
- package/hooks/git-pre-commit.sh +0 -76
- package/hooks/git-pre-push.sh +0 -17
- package/hooks/handover-guard.sh +0 -33
- package/hooks/kortext-init.sh +0 -194
- package/hooks/kortext-lib.sh +0 -141
- package/hooks/lint-guard.sh +0 -50
- package/hooks/secret-scanner.sh +0 -89
- package/hooks/size-guard.sh +0 -48
- package/hooks/snapshot-guard.sh +0 -39
- package/hooks/write-guard.sh +0 -77
- package/scripts/kortext-backlog-add.py +0 -123
- package/scripts/kortext-backlog-health.py +0 -112
- package/scripts/kortext-backlog-sync.py +0 -117
- package/scripts/kortext-bulk-plan.py +0 -63
- package/scripts/kortext-cli.py +0 -295
- package/scripts/kortext-consistency-check.py +0 -74
- package/scripts/kortext-context-check.py +0 -146
- package/scripts/kortext-handover.py +0 -107
- package/scripts/kortext-item-check.py +0 -61
- package/scripts/kortext-item-start.py +0 -70
- package/scripts/kortext-item-transition.py +0 -78
- package/scripts/kortext-lock.py +0 -174
- package/scripts/kortext-session-start.py +0 -127
- package/scripts/lock_kortext.sh +0 -34
- package/settings/.claude-settings.template.json +0 -48
- package/settings/CHANGELOG.md +0 -176
- package/settings/INTEGRATION-MAP.md +0 -276
- package/settings/README.md +0 -255
- package/settings/USER-GUIDE.md +0 -502
- package/settings/VERSION +0 -1
- package/settings/config.md +0 -8
- package/settings/runtime-adapters.md +0 -104
- 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/hooks/lint-guard.sh
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# KORTEXT MANAGED HOOK — generated/edited by kortext framework
|
|
3
|
-
# [KORTEXT] Kod Hatası Denetleyicisi (Lint-Guard)
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
# shellcheck source=kortext-lib.sh
|
|
7
|
-
source "$SCRIPT_DIR/kortext-lib.sh"
|
|
8
|
-
|
|
9
|
-
FILE_PATH="${KORTEXT_FILE_PATH:-${CLAUDE_FILE_PATH:-${GEMINI_FILE_PATH:-${OPENAI_FILE_PATH:-}}}}"
|
|
10
|
-
|
|
11
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then exit 0; fi
|
|
12
|
-
|
|
13
|
-
EXTENSION="${FILE_PATH##*.}"
|
|
14
|
-
|
|
15
|
-
echo "🔍 [KORTEXT LINT] Denetleniyor: $FILE_PATH"
|
|
16
|
-
|
|
17
|
-
case "$EXTENSION" in
|
|
18
|
-
sh)
|
|
19
|
-
# Shell script syntax kontrolü
|
|
20
|
-
bash -n "$FILE_PATH"
|
|
21
|
-
;;
|
|
22
|
-
js)
|
|
23
|
-
# JavaScript syntax kontrolü (Node.js varsa)
|
|
24
|
-
if command -v node > /dev/null 2>&1; then
|
|
25
|
-
node -c "$FILE_PATH"
|
|
26
|
-
fi
|
|
27
|
-
;;
|
|
28
|
-
json)
|
|
29
|
-
# JSON format kontrolü (Python varsa)
|
|
30
|
-
if command -v python3 > /dev/null 2>&1; then
|
|
31
|
-
python3 -m json.tool "$FILE_PATH" > /dev/null
|
|
32
|
-
fi
|
|
33
|
-
;;
|
|
34
|
-
*)
|
|
35
|
-
# Diğer dosyalar için şimdilik pas geç
|
|
36
|
-
exit 0
|
|
37
|
-
;;
|
|
38
|
-
esac
|
|
39
|
-
|
|
40
|
-
# Eğer yukarıdaki komutlardan biri hata verirse ($? 1 olursa) durdur
|
|
41
|
-
if [ $? -ne 0 ]; then
|
|
42
|
-
echo "---"
|
|
43
|
-
echo "❌ KORTEXT LINT HATASI"
|
|
44
|
-
echo "Dosya içinde yazım veya sözdizimi (syntax) hatası tespit edildi."
|
|
45
|
-
echo "Lütfen kodu düzeltip tekrar deneyin."
|
|
46
|
-
echo "---"
|
|
47
|
-
exit 1
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
exit 0
|
package/hooks/secret-scanner.sh
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# KORTEXT MANAGED HOOK — generated/edited by kortext framework
|
|
3
|
-
# [KORTEXT] Gizli Bilgi ve Sızıntı Tarayıcı
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
# shellcheck source=kortext-lib.sh
|
|
7
|
-
source "$SCRIPT_DIR/kortext-lib.sh"
|
|
8
|
-
|
|
9
|
-
kortext_load_config
|
|
10
|
-
KORTEXT_HOOK_MODE="${KORTEXT_HOOK_MODE:-permissive}"
|
|
11
|
-
|
|
12
|
-
# ---- Pattern Grubu 1: Tırnaklı Atama (api_key = "...") ----
|
|
13
|
-
P1="(api[_-]?key|secret[_-]?key|password|token|bearer|credential|private[_-]key|access[_-]?key|auth[_-]?token)\s*[:=]\s*['\"][^'\"]{8,}['\"]"
|
|
14
|
-
|
|
15
|
-
# ---- Pattern Grubu 2: Tırnaksız Atama (API_KEY=sk-abc123) ----
|
|
16
|
-
P2="(API_KEY|SECRET_KEY|ACCESS_KEY|AUTH_TOKEN|DB_PASSWORD|DATABASE_URL)=[^'\"\s]{8,}"
|
|
17
|
-
|
|
18
|
-
# ---- Pattern Grubu 3: Servis Spesifik Formatlar ----
|
|
19
|
-
# OpenAI: sk-proj-..., Anthropic: sk-ant-..., AWS: AKIA..., GitHub: ghp_..., Slack: xox*
|
|
20
|
-
P3="(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36,}|ghs_[a-zA-Z0-9]{36,}|xox[baprs]-[0-9\-a-zA-Z]{20,})"
|
|
21
|
-
|
|
22
|
-
# ---- Pattern Grubu 4: Authorization Header Değerleri ----
|
|
23
|
-
P4="Authorization:\s*Bearer\s+[a-zA-Z0-9._\-]{20,}"
|
|
24
|
-
|
|
25
|
-
# ---- İstisna: Env okuma ve placeholder değerler ----
|
|
26
|
-
EXCLUSIONS="process\.env|import\.meta\.env|os\.environ|getenv|YOUR_|<YOUR|PLACEHOLDER|example|EXAMPLE|\[VALUE\]|\[fill\]"
|
|
27
|
-
|
|
28
|
-
scan_file() {
|
|
29
|
-
local source_path="$1"
|
|
30
|
-
local display_path="${2:-$source_path}"
|
|
31
|
-
local pattern
|
|
32
|
-
|
|
33
|
-
# Behavior kuralı: ".env versiyon kontrolüne dahil edilmez, gizli bilgiler orada saklanır."
|
|
34
|
-
# Bu yüzden gerçek .env dosyalarını tarama dışı bırakıyoruz.
|
|
35
|
-
if [[ "$display_path" == *".env"* ]] && [[ "$display_path" != *".env.example"* ]]; then
|
|
36
|
-
return 0
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
[ -f "$source_path" ] || return 0
|
|
40
|
-
|
|
41
|
-
for pattern in "$P1" "$P2" "$P3" "$P4"; do
|
|
42
|
-
if grep -Ei "$pattern" "$source_path" 2>/dev/null | grep -vEi "$EXCLUSIONS" > /dev/null; then
|
|
43
|
-
echo "---"
|
|
44
|
-
echo "⚠️ KORTEXT GÜVENLİK İHLALİ: SIZINTI TESPİT EDİLDİ"
|
|
45
|
-
echo "Hata: Dosya içinde açık bir şifre, anahtar (key) veya token tespit edildi."
|
|
46
|
-
echo "Kortext Behavior Kuralları gereği (Madde 38-39):"
|
|
47
|
-
echo "1. Gizli bilgiler asla kod içine hardcoded yazılmaz."
|
|
48
|
-
echo "2. Sadece .env dosyası kullanılmalı ve değerler tırnak içinde oraya taşınmalıdır."
|
|
49
|
-
echo ""
|
|
50
|
-
echo "Dosya: $display_path"
|
|
51
|
-
echo "---"
|
|
52
|
-
return 1
|
|
53
|
-
fi
|
|
54
|
-
done
|
|
55
|
-
|
|
56
|
-
return 0
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if [ "${1:-}" = "--stdin" ]; then
|
|
60
|
-
DISPLAY_PATH="${2:-STDIN}"
|
|
61
|
-
TMP_FILE="$(mktemp)"
|
|
62
|
-
cat > "$TMP_FILE"
|
|
63
|
-
scan_file "$TMP_FILE" "$DISPLAY_PATH"
|
|
64
|
-
STATUS=$?
|
|
65
|
-
rm -f "$TMP_FILE"
|
|
66
|
-
exit "$STATUS"
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
if [ "$#" -gt 0 ]; then
|
|
70
|
-
STATUS=0
|
|
71
|
-
for FILE_PATH in "$@"; do
|
|
72
|
-
if ! scan_file "$FILE_PATH"; then
|
|
73
|
-
STATUS=1
|
|
74
|
-
fi
|
|
75
|
-
done
|
|
76
|
-
exit "$STATUS"
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
FILE_PATH="${KORTEXT_FILE_PATH:-${CLAUDE_FILE_PATH:-${GEMINI_FILE_PATH:-${OPENAI_FILE_PATH:-}}}}"
|
|
80
|
-
|
|
81
|
-
if [ -z "$FILE_PATH" ]; then
|
|
82
|
-
if [ "$KORTEXT_HOOK_MODE" = "strict" ]; then
|
|
83
|
-
echo "⚠️ [KORTEXT] KORTEXT_FILE_PATH tanımsız. strict modda çıkılıyor."
|
|
84
|
-
exit 1
|
|
85
|
-
fi
|
|
86
|
-
exit 0
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
scan_file "$FILE_PATH"
|
package/hooks/size-guard.sh
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# KORTEXT MANAGED HOOK — generated/edited by kortext framework
|
|
3
|
-
# [KORTEXT] Dosya Boyutu ve Arşivleme Muhafixı
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
# shellcheck source=kortext-lib.sh
|
|
7
|
-
source "$SCRIPT_DIR/kortext-lib.sh"
|
|
8
|
-
|
|
9
|
-
kortext_load_config
|
|
10
|
-
KORTEXT_HOOK_MODE="${KORTEXT_HOOK_MODE:-permissive}"
|
|
11
|
-
MAX_LINES="${KORTEXT_SIZE_LIMIT:-500}"
|
|
12
|
-
|
|
13
|
-
FILE_PATH="${KORTEXT_FILE_PATH:-${CLAUDE_FILE_PATH:-${GEMINI_FILE_PATH:-${OPENAI_FILE_PATH:-}}}}"
|
|
14
|
-
|
|
15
|
-
# Eğer dosya yolu boşsa veya dosya mevcut değilse çık
|
|
16
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
17
|
-
if [ -z "$FILE_PATH" ] && [ "$KORTEXT_HOOK_MODE" = "strict" ]; then
|
|
18
|
-
echo "⚠️ [KORTEXT] KORTEXT_FILE_PATH tanımsız. strict modda çıkılıyor."
|
|
19
|
-
exit 1
|
|
20
|
-
fi
|
|
21
|
-
exit 0
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
# ÖZEL İSTİSNA: backlog/ dizini her zaman tam kapsamlı kalmalıdır, arşivlenmez.
|
|
25
|
-
if [[ "$FILE_PATH" == *"backlog/"* ]]; then
|
|
26
|
-
exit 0
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
LINE_COUNT=$(wc -l < "$FILE_PATH")
|
|
30
|
-
|
|
31
|
-
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
|
|
32
|
-
echo "---"
|
|
33
|
-
echo "🛑 KORTEXT BOYUT SINIRI AŞILDI"
|
|
34
|
-
echo "Dosya: $FILE_PATH ($LINE_COUNT satır)"
|
|
35
|
-
echo "Limit: $MAX_LINES satır"
|
|
36
|
-
echo ""
|
|
37
|
-
echo "⚠️ ARCHIVING PROTOCOL GEREĞİ:"
|
|
38
|
-
echo "1. Dosyadaki eski veya tamamlanmış verileri tespit et."
|
|
39
|
-
echo "2. Bu verileri '$KORTEXT_REL/workspace/archive/' altına '[dosya-adi]_[YYYY-MM-DD_HHMMSS].md' formatıyla taşı."
|
|
40
|
-
echo "3. Ana dosyada arşivlenen bölüme bir link/not bırak."
|
|
41
|
-
echo "4. Ana dosyayı hafifletip işlemi tekrar dene."
|
|
42
|
-
echo ""
|
|
43
|
-
echo "Not: 'backlog/' dizinindeki dosyalar bu kuraldan muaf tutulmuştur."
|
|
44
|
-
echo "---"
|
|
45
|
-
exit 1
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
exit 0
|
package/hooks/snapshot-guard.sh
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# KORTEXT MANAGED HOOK — generated/edited by kortext framework
|
|
3
|
-
# [KORTEXT] Anlık Yedekleme Muhafızı (Snapshot-Guard)
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
# shellcheck source=kortext-lib.sh
|
|
7
|
-
source "$SCRIPT_DIR/kortext-lib.sh"
|
|
8
|
-
|
|
9
|
-
kortext_load_config
|
|
10
|
-
KORTEXT_HOOK_MODE="${KORTEXT_HOOK_MODE:-permissive}"
|
|
11
|
-
BACKUP_KEEP="${KORTEXT_BACKUP_KEEP:-5}"
|
|
12
|
-
|
|
13
|
-
FILE_PATH="${KORTEXT_FILE_PATH:-${CLAUDE_FILE_PATH:-${GEMINI_FILE_PATH:-${OPENAI_FILE_PATH:-}}}}"
|
|
14
|
-
BACKUP_DIR="$KORTEXT_WORKSPACE_DIR/backups"
|
|
15
|
-
|
|
16
|
-
# Dosya yolu yoksa veya mevcut değilse çık
|
|
17
|
-
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
|
|
18
|
-
if [ -z "$FILE_PATH" ] && [ "$KORTEXT_HOOK_MODE" = "strict" ]; then
|
|
19
|
-
echo "⚠️ [KORTEXT] KORTEXT_FILE_PATH tanımsız. strict modda çıkılıyor."
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Yedekleme dizinini oluştur
|
|
26
|
-
mkdir -p "$BACKUP_DIR"
|
|
27
|
-
|
|
28
|
-
FILENAME=$(basename "$FILE_PATH")
|
|
29
|
-
TIMESTAMP=$(date "+%Y-%m-%d_%H%M%S") # Tarih-Saat-Dakika-Saniye
|
|
30
|
-
BACKUP_NAME="${BACKUP_DIR}/${FILENAME}_${TIMESTAMP}.bak"
|
|
31
|
-
|
|
32
|
-
# 1. Yedeği al
|
|
33
|
-
cp "$FILE_PATH" "$BACKUP_NAME"
|
|
34
|
-
|
|
35
|
-
# 2. Temizlik: Aynı dosya için olan yedeklerden sadece en yeni 5 tanesini tut
|
|
36
|
-
# (Diğerlerini silerek yer tasarrufu sağlar)
|
|
37
|
-
ls -t "${BACKUP_DIR}/${FILENAME}"_* 2>/dev/null | tail -n +$((BACKUP_KEEP + 1)) | xargs rm -f 2>/dev/null
|
|
38
|
-
|
|
39
|
-
exit 0
|
package/hooks/write-guard.sh
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# KORTEXT MANAGED HOOK — generated/edited by kortext framework
|
|
3
|
-
# [KORTEXT] Yazma Muhafızı (Gelişmiş)
|
|
4
|
-
# 1. Konum Kontrolü: Sadece izin verilen dizinlere yazılmasını sağlar.
|
|
5
|
-
# 2. Kalite Kontrolü (Tamlık Kapısı): Yarım kalmış içeriklerin yazılmasını engeller.
|
|
6
|
-
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
-
# shellcheck source=kortext-lib.sh
|
|
9
|
-
source "$SCRIPT_DIR/kortext-lib.sh"
|
|
10
|
-
|
|
11
|
-
kortext_load_config
|
|
12
|
-
KORTEXT_HOOK_MODE="${KORTEXT_HOOK_MODE:-permissive}"
|
|
13
|
-
|
|
14
|
-
# Girdiyi Oku (JSON formatında gelmesi beklenir)
|
|
15
|
-
INPUT=$(cat)
|
|
16
|
-
FILE_PATH="$(kortext_json_field "$INPUT" '.tool_input.file_path')"
|
|
17
|
-
|
|
18
|
-
# Eğer JSON'dan gelmiyorsa çevresel değişkenleri dene
|
|
19
|
-
if [ -z "$FILE_PATH" ]; then
|
|
20
|
-
FILE_PATH="${KORTEXT_FILE_PATH:-${CLAUDE_FILE_PATH:-${GEMINI_FILE_PATH:-${OPENAI_FILE_PATH:-}}}}"
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
if [ -z "$FILE_PATH" ]; then
|
|
24
|
-
if [ "$KORTEXT_HOOK_MODE" = "strict" ]; then
|
|
25
|
-
echo "⚠️ [KORTEXT] Dosya yolu tespit edilemedi. strict modda çıkılıyor."
|
|
26
|
-
exit 1
|
|
27
|
-
fi
|
|
28
|
-
exit 0
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# 1. BÖLÜM: KONUM KONTROLÜ
|
|
32
|
-
ALLOWED=false
|
|
33
|
-
if kortext_is_workspace_path "$FILE_PATH" || kortext_is_env_path "$FILE_PATH"; then
|
|
34
|
-
ALLOWED=true
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
if [ "$ALLOWED" = false ]; then
|
|
38
|
-
echo "---"
|
|
39
|
-
echo "❌ KORTEXT ERİŞİM ENGELİ"
|
|
40
|
-
echo "Hata: Kortext kuralları gereği Framework çekirdek dizinlerine yazma yetkiniz yoktur."
|
|
41
|
-
echo "Lütfen çıktılarınızı '$KORTEXT_REL/workspace/' altındaki uygun klasörlere kaydedin."
|
|
42
|
-
echo "Hedeflenen Dosya: $FILE_PATH"
|
|
43
|
-
echo "---"
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# 2. BÖLÜM: TAMLIK KAPISI (KALİTE KONTROLÜ)
|
|
48
|
-
# Yazılacak içeriği al (Write veya Edit/MultiEdit araçları için)
|
|
49
|
-
CONTENT="$(kortext_json_field "$INPUT" '.tool_input.content // .tool_input.new_string')"
|
|
50
|
-
|
|
51
|
-
if [ -n "$CONTENT" ]; then
|
|
52
|
-
# Yarım bırakma işaretçileri (TBD, FIXME, PLACEHOLDER vb.)
|
|
53
|
-
# Önemli İstisna: "// todo: [tech-debt]" formatındaki teknik borçlara izin verilir.
|
|
54
|
-
if echo "$CONTENT" | grep -qiE '\bTBD\b|\bFIXME\b|\[PLACEHOLDER\]|\[INSERT ' || \
|
|
55
|
-
(echo "$CONTENT" | grep -i '\bTODO\b' | grep -vi '\[tech-debt\]' | grep -q .); then
|
|
56
|
-
echo "---"
|
|
57
|
-
echo "⚠️ TAMLIK KAPISI: EKSİK İÇERİK TESPİT EDİLDİ"
|
|
58
|
-
echo "Hata: Yazmaya çalıştığınız içerikte disiplinsiz (etiketsiz) yer tutucular var."
|
|
59
|
-
echo "Kortext Politikası: Sıradan TODO/TBD kullanımı yasaktır."
|
|
60
|
-
echo "İstisna: Eğer teknik borç bırakmanız gerekiyorsa '// todo: [tech-debt] açıklama' formatını kullanın."
|
|
61
|
-
echo "Dosya: $FILE_PATH"
|
|
62
|
-
echo "---"
|
|
63
|
-
exit 1
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
# Kararsızlık ifadeleri
|
|
67
|
-
if echo "$CONTENT" | grep -qiE 'assess whether|decide later|need to determine|open question|to be decided|deferred decision'; then
|
|
68
|
-
echo "---"
|
|
69
|
-
echo "⚠️ TAMLIK KAPISI: BELİRSİZLİK TESPİT EDİLDİ"
|
|
70
|
-
echo "Hata: İçerikte 'sonra karar verilecek' veya 'belirlenmesi gerekiyor' gibi ucu açık ifadeler var."
|
|
71
|
-
echo "Kortext Politikası: Yazma işleminden önce tüm kararlar çözülmüş olmalıdır."
|
|
72
|
-
echo "---"
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
exit 0
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import argparse
|
|
5
|
-
import shutil
|
|
6
|
-
import subprocess
|
|
7
|
-
from datetime import datetime
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
BACKLOG_ROOT = Path("workspace/memory/backlog")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _atomic_commit(message: str, paths: list[str]) -> None:
|
|
15
|
-
# Kortext atomic operation: dosyaları add edip commit'ler.
|
|
16
|
-
if not shutil.which("git"):
|
|
17
|
-
return
|
|
18
|
-
try:
|
|
19
|
-
subprocess.run(["git", "add", "--", *paths], check=True, capture_output=True)
|
|
20
|
-
subprocess.run(
|
|
21
|
-
["git", "commit", "-m", message, "--", *paths],
|
|
22
|
-
check=True,
|
|
23
|
-
capture_output=True,
|
|
24
|
-
)
|
|
25
|
-
except subprocess.CalledProcessError:
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def slugify(title: str) -> str:
|
|
30
|
-
return "-".join(title.lower().split())
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def dashboard_path(kind: str) -> Path:
|
|
34
|
-
mapping = {
|
|
35
|
-
"version": BACKLOG_ROOT / "version-dashboard.md",
|
|
36
|
-
"epic": BACKLOG_ROOT / "epic-dashboard.md",
|
|
37
|
-
"debt": BACKLOG_ROOT / "debt-dashboard.md",
|
|
38
|
-
}
|
|
39
|
-
return mapping[kind]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def append_row(path: Path, row: str) -> None:
|
|
43
|
-
content = path.read_text() if path.exists() else ""
|
|
44
|
-
if row not in content:
|
|
45
|
-
if content and not content.endswith("\n"):
|
|
46
|
-
content += "\n"
|
|
47
|
-
content += row + "\n"
|
|
48
|
-
path.write_text(content)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def build_item_content(item_type: str, item_id: str, title: str, persona: str, epic: str | None) -> str:
|
|
52
|
-
date_str = datetime.now().strftime("%Y.%m.%d-%H:%M")
|
|
53
|
-
epic_line = f"> **Epic:** {epic}\n" if epic else ""
|
|
54
|
-
return f"""# {item_id}: {title}
|
|
55
|
-
{epic_line}> **Assignee:** [{persona}]
|
|
56
|
-
> **Model:** [selected-ai-model]
|
|
57
|
-
> **Status:** To Do
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## Description
|
|
62
|
-
{title} açıklaması.
|
|
63
|
-
|
|
64
|
-
## Acceptance Criteria
|
|
65
|
-
- [ ] Code review
|
|
66
|
-
- [ ] Quality control
|
|
67
|
-
- [ ] Security check
|
|
68
|
-
|
|
69
|
-
## Dependencies
|
|
70
|
-
- [task-id] - [task-name]
|
|
71
|
-
|
|
72
|
-
## Work Log
|
|
73
|
-
- **[{date_str}] ({persona}):** {item_type.capitalize()} oluşturuldu.
|
|
74
|
-
|
|
75
|
-
## Decisions
|
|
76
|
-
- [alınan karar]
|
|
77
|
-
|
|
78
|
-
## Notes
|
|
79
|
-
- [notlar]
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def main() -> None:
|
|
84
|
-
parser = argparse.ArgumentParser(description="Kortext Backlog Add Tool")
|
|
85
|
-
parser.add_argument("--type", choices=["task", "bug", "debt"], required=True)
|
|
86
|
-
parser.add_argument("--id", required=True, help="e.g. T01, B01, D01")
|
|
87
|
-
parser.add_argument("--title", required=True)
|
|
88
|
-
parser.add_argument("--persona", default="+persona")
|
|
89
|
-
parser.add_argument("--epic", help="e.g. E01-auth (required for task and bug)")
|
|
90
|
-
parser.add_argument("--no-commit", action="store_true", help="Atomic git commit'i atla")
|
|
91
|
-
args = parser.parse_args()
|
|
92
|
-
|
|
93
|
-
if args.type in {"task", "bug"} and not args.epic:
|
|
94
|
-
raise SystemExit("Hata: task ve bug türleri için --epic zorunludur.")
|
|
95
|
-
|
|
96
|
-
BACKLOG_ROOT.mkdir(parents=True, exist_ok=True)
|
|
97
|
-
file_path = BACKLOG_ROOT / f"{args.id}-{slugify(args.title)}.md"
|
|
98
|
-
file_path.write_text(build_item_content(args.type, args.id, args.title, args.persona, args.epic))
|
|
99
|
-
|
|
100
|
-
if args.type in {"task", "bug"}:
|
|
101
|
-
dashboard = dashboard_path("epic")
|
|
102
|
-
append_row(
|
|
103
|
-
dashboard,
|
|
104
|
-
f"| {args.id} | [{args.title}](./{file_path.name}) | {args.epic} | {args.persona} | To Do |",
|
|
105
|
-
)
|
|
106
|
-
else:
|
|
107
|
-
dashboard = dashboard_path("debt")
|
|
108
|
-
append_row(
|
|
109
|
-
dashboard,
|
|
110
|
-
f"| {args.id} | [{args.title}](./{file_path.name}) | {args.persona} | To Do |",
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
print(f"✅ {args.type.capitalize()} oluşturuldu: {file_path}")
|
|
114
|
-
|
|
115
|
-
if not args.no_commit:
|
|
116
|
-
_atomic_commit(
|
|
117
|
-
f"chore(kortext): add {args.type} {args.id} [skip ci]",
|
|
118
|
-
[str(file_path), str(dashboard)],
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if __name__ == "__main__":
|
|
123
|
-
main()
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Kortext Backlog Health
|
|
4
|
-
----------------------
|
|
5
|
-
Backlog'un genel sağlık durumunu raporlar. Blocker itemlar, stale In Progress'ler,
|
|
6
|
-
review bekleyenler ve hazır (başlanabilir) itemlar özetlenir.
|
|
7
|
-
"""
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
import re
|
|
12
|
-
from datetime import datetime
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ROOT = Path(__file__).resolve().parents[1]
|
|
17
|
-
BACKLOG_DIR = ROOT / "workspace/memory/backlog"
|
|
18
|
-
# In Progress backlog item'larının stale eşiği. Env var ile override edilebilir.
|
|
19
|
-
STALE_HOURS = int(os.environ.get("KORTEXT_STALE_HOURS_BACKLOG", "48"))
|
|
20
|
-
|
|
21
|
-
STATUS_PATTERN = re.compile(r"\*\*Status:\*\*\s*(.+)")
|
|
22
|
-
BLOCKED_BY_PATTERN = re.compile(r"\*\*Blocked By:\*\*\s*(.+)")
|
|
23
|
-
RISK_PATTERN = re.compile(r"\*\*Risk:\*\*\s*(Blocker|High|Critical)", re.IGNORECASE)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def parse_items() -> list[dict]:
|
|
27
|
-
items = []
|
|
28
|
-
if not BACKLOG_DIR.exists():
|
|
29
|
-
return items
|
|
30
|
-
for f in sorted(BACKLOG_DIR.glob("[TBD]*.md")):
|
|
31
|
-
text = f.read_text(errors="ignore")
|
|
32
|
-
status_m = STATUS_PATTERN.search(text)
|
|
33
|
-
blocked_m = BLOCKED_BY_PATTERN.search(text)
|
|
34
|
-
risk_m = RISK_PATTERN.search(text)
|
|
35
|
-
|
|
36
|
-
status = status_m.group(1).strip() if status_m else "?"
|
|
37
|
-
has_blocker = blocked_m is not None and blocked_m.group(1).strip() not in ("", "-", "Yok")
|
|
38
|
-
is_high_risk = risk_m is not None
|
|
39
|
-
mtime = datetime.fromtimestamp(f.stat().st_mtime)
|
|
40
|
-
age_hours = (datetime.now() - mtime).total_seconds() / 3600
|
|
41
|
-
|
|
42
|
-
items.append({
|
|
43
|
-
"id": f.stem,
|
|
44
|
-
"status": status,
|
|
45
|
-
"has_blocker": has_blocker,
|
|
46
|
-
"is_high_risk": is_high_risk,
|
|
47
|
-
"age_hours": round(age_hours, 1),
|
|
48
|
-
"stale": status == "In Progress" and age_hours > STALE_HOURS,
|
|
49
|
-
})
|
|
50
|
-
return items
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def compute_score(items: list[dict]) -> tuple[int, int]:
|
|
54
|
-
"""Basit sağlık skoru: toplam item sayısı üzerinden sorunlu itemları düşür."""
|
|
55
|
-
total = len(items)
|
|
56
|
-
if total == 0:
|
|
57
|
-
return 100, 100
|
|
58
|
-
problems = sum(1 for i in items if i["stale"] or i["has_blocker"])
|
|
59
|
-
score = max(0, round((1 - problems / total) * 100))
|
|
60
|
-
return score, 100
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def main() -> None:
|
|
64
|
-
items = parse_items()
|
|
65
|
-
now = datetime.now().strftime("%d.%m.%Y %H:%M")
|
|
66
|
-
|
|
67
|
-
# Grupla
|
|
68
|
-
todo = [i for i in items if i["status"] == "To Do" and not i["has_blocker"]]
|
|
69
|
-
in_progress = [i for i in items if i["status"] == "In Progress"]
|
|
70
|
-
review = [i for i in items if i["status"] == "Review"]
|
|
71
|
-
blocked = [i for i in items if i["has_blocker"] or i["status"] == "Blocked"]
|
|
72
|
-
stale = [i for i in items if i["stale"]]
|
|
73
|
-
done = [i for i in items if i["status"] == "Done"]
|
|
74
|
-
score, max_score = compute_score(items)
|
|
75
|
-
|
|
76
|
-
print("=" * 60)
|
|
77
|
-
print(f" KORTEXT BACKLOG HEALTH — {now}")
|
|
78
|
-
print("=" * 60)
|
|
79
|
-
print(f"\n Toplam item : {len(items)}")
|
|
80
|
-
print(f" ✅ Başlanabilir : {len(todo)}")
|
|
81
|
-
print(f" 🔄 In Progress : {len(in_progress)}")
|
|
82
|
-
print(f" 👀 Review bekliyor : {len(review)}")
|
|
83
|
-
print(f" 🚫 Bloklu : {len(blocked)}")
|
|
84
|
-
print(f" ⚠️ Stale ({STALE_HOURS}s+) : {len(stale)}")
|
|
85
|
-
print(f" ✔️ Done : {len(done)}")
|
|
86
|
-
|
|
87
|
-
if blocked:
|
|
88
|
-
print(f"\n 🚫 BLOKLANAN İTEMLAR:")
|
|
89
|
-
for i in blocked:
|
|
90
|
-
print(f" • {i['id']} ({i['status']})")
|
|
91
|
-
|
|
92
|
-
if stale:
|
|
93
|
-
print(f"\n ⚠️ STALE İTEMLAR:")
|
|
94
|
-
for i in stale:
|
|
95
|
-
print(f" • {i['id']} — {i['age_hours']} saat önce güncellendi")
|
|
96
|
-
|
|
97
|
-
if review:
|
|
98
|
-
print(f"\n 👀 REVIEW BEKLEYENLER (+prime aksiyonu gerekebilir):")
|
|
99
|
-
for i in review:
|
|
100
|
-
print(f" • {i['id']}")
|
|
101
|
-
|
|
102
|
-
bar_filled = int(score / 5)
|
|
103
|
-
bar = "█" * bar_filled + "░" * (20 - bar_filled)
|
|
104
|
-
print(f"\n Sağlık Skoru: {score}/{max_score} [{bar}]")
|
|
105
|
-
print("=" * 60)
|
|
106
|
-
|
|
107
|
-
if score < 50:
|
|
108
|
-
raise SystemExit(1)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if __name__ == "__main__":
|
|
112
|
-
main()
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import re
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
BACKLOG_ROOT = Path("workspace/memory/backlog")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Item:
|
|
14
|
-
item_id: str
|
|
15
|
-
title: str
|
|
16
|
-
status: str
|
|
17
|
-
path: Path
|
|
18
|
-
epic: str | None
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def parse_item(path: Path) -> Item:
|
|
22
|
-
content = path.read_text()
|
|
23
|
-
header = re.search(r"^# ([TBD]\d+): (.+)$", content, re.M)
|
|
24
|
-
status = re.search(r"> \*\*Status:\*\* (.+)", content)
|
|
25
|
-
epic = re.search(r"> \*\*Epic:\*\* (.+)", content)
|
|
26
|
-
if not header or not status:
|
|
27
|
-
raise ValueError(f"Geçersiz item şablonu: {path}")
|
|
28
|
-
return Item(
|
|
29
|
-
item_id=header.group(1),
|
|
30
|
-
title=header.group(2),
|
|
31
|
-
status=status.group(1).strip(),
|
|
32
|
-
path=path,
|
|
33
|
-
epic=epic.group(1).strip() if epic else None,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def actual_items() -> list[Item]:
|
|
38
|
-
items: list[Item] = []
|
|
39
|
-
for path in BACKLOG_ROOT.glob("*.md"):
|
|
40
|
-
if path.name.endswith("dashboard.md"):
|
|
41
|
-
continue
|
|
42
|
-
if re.match(r"^[TBD]\d+-", path.name):
|
|
43
|
-
items.append(parse_item(path))
|
|
44
|
-
return items
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def expected_epic_rows(items: list[Item]) -> set[str]:
|
|
48
|
-
return {
|
|
49
|
-
f"| {item.item_id} | [{item.title}](./{item.path.name}) | {item.epic} | "
|
|
50
|
-
f"{read_assignee(item.path)} | {item.status} |"
|
|
51
|
-
for item in items
|
|
52
|
-
if item.item_id.startswith(("T", "B"))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def expected_debt_rows(items: list[Item]) -> set[str]:
|
|
57
|
-
return {
|
|
58
|
-
f"| {item.item_id} | [{item.title}](./{item.path.name}) | "
|
|
59
|
-
f"{read_assignee(item.path)} | {item.status} |"
|
|
60
|
-
for item in items
|
|
61
|
-
if item.item_id.startswith("D")
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def read_assignee(path: Path) -> str:
|
|
66
|
-
match = re.search(r"> \*\*Assignee:\*\* \[(.+)\]", path.read_text())
|
|
67
|
-
return match.group(1) if match else "+persona"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def actual_rows(path: Path) -> set[str]:
|
|
71
|
-
if not path.exists():
|
|
72
|
-
return set()
|
|
73
|
-
return {
|
|
74
|
-
line
|
|
75
|
-
for line in path.read_text().splitlines()
|
|
76
|
-
if re.match(r"^\| [TBD]\d+ \|", line)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def compare(label: str, expected: set[str], actual: set[str]) -> list[str]:
|
|
81
|
-
failures: list[str] = []
|
|
82
|
-
for row in sorted(expected - actual):
|
|
83
|
-
failures.append(f"{label}: eksik satır -> {row}")
|
|
84
|
-
for row in sorted(actual - expected):
|
|
85
|
-
failures.append(f"{label}: beklenmeyen satır -> {row}")
|
|
86
|
-
return failures
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def main() -> None:
|
|
90
|
-
items = actual_items()
|
|
91
|
-
failures: list[str] = []
|
|
92
|
-
failures.extend(
|
|
93
|
-
compare(
|
|
94
|
-
"epic-dashboard.md",
|
|
95
|
-
expected_epic_rows(items),
|
|
96
|
-
actual_rows(BACKLOG_ROOT / "epic-dashboard.md"),
|
|
97
|
-
)
|
|
98
|
-
)
|
|
99
|
-
failures.extend(
|
|
100
|
-
compare(
|
|
101
|
-
"debt-dashboard.md",
|
|
102
|
-
expected_debt_rows(items),
|
|
103
|
-
actual_rows(BACKLOG_ROOT / "debt-dashboard.md"),
|
|
104
|
-
)
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
if failures:
|
|
108
|
-
print("❌ Backlog dashboard drift tespit edildi:")
|
|
109
|
-
for failure in failures:
|
|
110
|
-
print(f"- {failure}")
|
|
111
|
-
raise SystemExit(1)
|
|
112
|
-
|
|
113
|
-
print("✅ Backlog dashboard'ları item dosyalarıyla uyumlu.")
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if __name__ == "__main__":
|
|
117
|
-
main()
|