From ecd8a8df3796cb4a6a93c3f6d536fd530cd6c3b1 Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 13 Jun 2026 10:03:10 +0900 Subject: [PATCH] init: agent-ops, agent-roadmap, cursorrules, and project configuration files --- .aiexclude | 4 + .claude/settings.json | 8 + .clineignore | 4 + .clinerules | 44 ++ .cursorignore | 4 + .cursorrules | 44 ++ .geminiignore | 4 + .gitignore | 15 + AGENTS.md | 56 ++- CLAUDE.md | 44 ++ GEMINI.md | 44 ++ README.md | 178 +++++-- agent-ops/.version | 1 + agent-ops/bin/ai-ignore.sh | 268 ++++++++++ agent-ops/bin/bump-version.sh | 28 ++ agent-ops/bin/entry-files.sh | 24 + agent-ops/bin/init-agent-ops.sh | 343 +++++++++++++ agent-ops/bin/roadmap-dependency-checker.sh | 370 ++++++++++++++ agent-ops/bin/sync.sh | 338 +++++++++++++ .../common/_templates/domain-rule-template.md | 35 ++ .../_templates/test-case-rule-template.md | 79 +++ .../_templates/test-env-rules-template.md | 38 ++ agent-ops/rules/common/philosophy.md | 98 ++++ agent-ops/rules/common/rules-roadmap.md | 158 ++++++ agent-ops/rules/common/rules.md | 44 ++ ...lementation-user-review-request-section.md | 11 + .../_templates/roadmap-current-template.md | 29 ++ .../_templates/roadmap-milestone-template.md | 85 ++++ .../_templates/roadmap-phase-template.md | 23 + .../roadmap-position-report-template.md | 21 + .../common/_templates/roadmap-template.md | 42 ++ .../common/_templates/skill-template.md | 56 +++ .../common/analyze-roadmap-position/SKILL.md | 74 +++ .../common/check-roadmap-dependency/SKILL.md | 91 ++++ agent-ops/skills/common/code-review/SKILL.md | 455 +++++++++++++++++ .../common/code-review/agents/openai.yaml | 4 + .../templates/complete-log-template.md | 41 ++ .../templates/user-review-template.md | 52 ++ agent-ops/skills/common/commit-push/SKILL.md | 162 ++++++ .../skills/common/create-domain-rule/SKILL.md | 120 +++++ .../skills/common/create-readme/SKILL.md | 156 ++++++ .../skills/common/create-roadmap/SKILL.md | 179 +++++++ agent-ops/skills/common/create-skill/SKILL.md | 101 ++++ agent-ops/skills/common/create-test/SKILL.md | 137 +++++ .../skills/common/init-agent-ops/SKILL.md | 281 +++++++++++ agent-ops/skills/common/plan/SKILL.md | 469 ++++++++++++++++++ .../skills/common/plan/agents/openai.yaml | 4 + agent-ops/skills/common/router.md | 21 + agent-ops/skills/common/sync-pull/SKILL.md | 43 ++ agent-ops/skills/common/sync-push/SKILL.md | 115 +++++ .../skills/common/update-domain-rule/SKILL.md | 117 +++++ .../skills/common/update-roadmap/SKILL.md | 424 ++++++++++++++++ agent-ops/skills/common/update-test/SKILL.md | 99 ++++ agent-roadmap/ROADMAP.md | 52 ++ .../phase/agent-shell-iop-execution/PHASE.md | 35 ++ .../milestones/agent-shell-runtime-channel.md | 68 +++ .../command-policy-and-cancellation.md | 78 +++ .../milestones/iop-agent-run-bridge.md | 78 +++ .../phase/control-plane-foundation/PHASE.md | 40 ++ .../milestones/operation-event-outbox.md | 75 +++ .../platformless-git-operation-engine.md | 76 +++ .../repo-registry-and-workspace-lease.md | 75 +++ .../runtime-scaffold-and-contract-baseline.md | 72 +++ .../control-surface-operations-ux/PHASE.md | 35 ++ .../flutter-control-surface-baseline.md | 77 +++ .../milestones/multi-runner-management.md | 73 +++ .../milestones/operations-observability.md | 76 +++ .../PHASE.md | 35 ++ .../milestones/change-request-abstraction.md | 76 +++ .../milestones/provider-adapter-foundation.md | 77 +++ .../milestones/webhook-revision-reconcile.md | 76 +++ agent-test/local/rules.md | 10 + opencode.json | 17 + 73 files changed, 6830 insertions(+), 56 deletions(-) create mode 100644 .aiexclude create mode 100644 .claude/settings.json create mode 100644 .clineignore create mode 100644 .clinerules create mode 100644 .cursorignore create mode 100644 .cursorrules create mode 100644 .geminiignore create mode 100644 CLAUDE.md create mode 100644 GEMINI.md create mode 100644 agent-ops/.version create mode 100755 agent-ops/bin/ai-ignore.sh create mode 100755 agent-ops/bin/bump-version.sh create mode 100644 agent-ops/bin/entry-files.sh create mode 100755 agent-ops/bin/init-agent-ops.sh create mode 100755 agent-ops/bin/roadmap-dependency-checker.sh create mode 100755 agent-ops/bin/sync.sh create mode 100644 agent-ops/rules/common/_templates/domain-rule-template.md create mode 100644 agent-ops/rules/common/_templates/test-case-rule-template.md create mode 100644 agent-ops/rules/common/_templates/test-env-rules-template.md create mode 100644 agent-ops/rules/common/philosophy.md create mode 100644 agent-ops/rules/common/rules-roadmap.md create mode 100644 agent-ops/rules/common/rules.md create mode 100644 agent-ops/skills/common/_templates/implementation-user-review-request-section.md create mode 100644 agent-ops/skills/common/_templates/roadmap-current-template.md create mode 100644 agent-ops/skills/common/_templates/roadmap-milestone-template.md create mode 100644 agent-ops/skills/common/_templates/roadmap-phase-template.md create mode 100644 agent-ops/skills/common/_templates/roadmap-position-report-template.md create mode 100644 agent-ops/skills/common/_templates/roadmap-template.md create mode 100644 agent-ops/skills/common/_templates/skill-template.md create mode 100644 agent-ops/skills/common/analyze-roadmap-position/SKILL.md create mode 100644 agent-ops/skills/common/check-roadmap-dependency/SKILL.md create mode 100644 agent-ops/skills/common/code-review/SKILL.md create mode 100644 agent-ops/skills/common/code-review/agents/openai.yaml create mode 100644 agent-ops/skills/common/code-review/templates/complete-log-template.md create mode 100644 agent-ops/skills/common/code-review/templates/user-review-template.md create mode 100644 agent-ops/skills/common/commit-push/SKILL.md create mode 100644 agent-ops/skills/common/create-domain-rule/SKILL.md create mode 100644 agent-ops/skills/common/create-readme/SKILL.md create mode 100644 agent-ops/skills/common/create-roadmap/SKILL.md create mode 100644 agent-ops/skills/common/create-skill/SKILL.md create mode 100644 agent-ops/skills/common/create-test/SKILL.md create mode 100644 agent-ops/skills/common/init-agent-ops/SKILL.md create mode 100644 agent-ops/skills/common/plan/SKILL.md create mode 100644 agent-ops/skills/common/plan/agents/openai.yaml create mode 100644 agent-ops/skills/common/router.md create mode 100644 agent-ops/skills/common/sync-pull/SKILL.md create mode 100644 agent-ops/skills/common/sync-push/SKILL.md create mode 100644 agent-ops/skills/common/update-domain-rule/SKILL.md create mode 100644 agent-ops/skills/common/update-roadmap/SKILL.md create mode 100644 agent-ops/skills/common/update-test/SKILL.md create mode 100644 agent-roadmap/ROADMAP.md create mode 100644 agent-roadmap/phase/agent-shell-iop-execution/PHASE.md create mode 100644 agent-roadmap/phase/agent-shell-iop-execution/milestones/agent-shell-runtime-channel.md create mode 100644 agent-roadmap/phase/agent-shell-iop-execution/milestones/command-policy-and-cancellation.md create mode 100644 agent-roadmap/phase/agent-shell-iop-execution/milestones/iop-agent-run-bridge.md create mode 100644 agent-roadmap/phase/control-plane-foundation/PHASE.md create mode 100644 agent-roadmap/phase/control-plane-foundation/milestones/operation-event-outbox.md create mode 100644 agent-roadmap/phase/control-plane-foundation/milestones/platformless-git-operation-engine.md create mode 100644 agent-roadmap/phase/control-plane-foundation/milestones/repo-registry-and-workspace-lease.md create mode 100644 agent-roadmap/phase/control-plane-foundation/milestones/runtime-scaffold-and-contract-baseline.md create mode 100644 agent-roadmap/phase/control-surface-operations-ux/PHASE.md create mode 100644 agent-roadmap/phase/control-surface-operations-ux/milestones/flutter-control-surface-baseline.md create mode 100644 agent-roadmap/phase/control-surface-operations-ux/milestones/multi-runner-management.md create mode 100644 agent-roadmap/phase/control-surface-operations-ux/milestones/operations-observability.md create mode 100644 agent-roadmap/phase/provider-change-request-integration/PHASE.md create mode 100644 agent-roadmap/phase/provider-change-request-integration/milestones/change-request-abstraction.md create mode 100644 agent-roadmap/phase/provider-change-request-integration/milestones/provider-adapter-foundation.md create mode 100644 agent-roadmap/phase/provider-change-request-integration/milestones/webhook-revision-reconcile.md create mode 100644 opencode.json diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.aiexclude @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0c2baa3 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "deny": [ + "Read(./agent-task/archive/**)" + ] + } +} diff --git a/.clineignore b/.clineignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.clineignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.clinerules b/.clinerules new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/.clinerules @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/.geminiignore b/.geminiignore new file mode 100644 index 0000000..17410c3 --- /dev/null +++ b/.geminiignore @@ -0,0 +1,4 @@ +# BEGIN Agent-Ops managed ignore +agent-task/archive/** +agent-roadmap/archive/** +# END Agent-Ops managed ignore diff --git a/.gitignore b/.gitignore index 01c1827..99015c7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,18 @@ build/ .gito/ tmp/ + +# BEGIN Agent-Ops managed gitignore +!agent-task/ +!agent-task/**/ +!agent-task/**/*.md +!agent-task/**/*.log +agent-roadmap/current.md +# END Agent-Ops managed gitignore + +# Agent-Ops Private Rules +agent-ops/rules/private/ + +# Agent-Test Local Environment +agent-test/local/ +agent-test/runs/ diff --git a/AGENTS.md b/AGENTS.md index b25954b..d0488a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,34 +1,44 @@ -# Gito Agent Instructions +# 공통 규칙 -Read this file before making changes in this repository. +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** -## Common Rules +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. -- Prefer the existing structure over creating new top-level patterns. -- Keep user-facing final answers in Korean unless the user asks otherwise. -- Check the relevant domain rule before code changes. -- Do not expand scope beyond the current request. -- Do not write secrets, tokens, passwords, private endpoints, or raw credentials to tracked files. -- Use `rg` or `rg --files` for searches when available. -- Use root helpers under `bin/` before ad hoc commands when they cover the task. - -## Required First Reads - -Read these files once per session when they exist: +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. 1. `agent-ops/rules/project/rules.md` 2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` -## Domain Rules +# 프로젝트 간 잠금 -| Path | Domain | Rules | -| --- | --- | --- | -| `services/core/**` | core | `agent-ops/rules/project/domain/core/rules.md` | -| `apps/client/**` | client | `agent-ops/rules/project/domain/client/rules.md` | -| `packages/contracts/**` | contracts | `agent-ops/rules/project/domain/contracts/rules.md` | -| `bin/**`, `docs/**`, `AGENTS.md`, `README.md`, `agent-ops/**`, `agent-test/**` | workspace-ops | `agent-ops/rules/project/domain/workspace-ops/rules.md` | +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. -## Test Rules +# 스킬 규칙 -When validation is part of the task, read `agent-test/local/rules.md` first. +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/README.md b/README.md index 8bebcf0..4cd2f81 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,100 @@ # Gito -Gito is a platformless Git control plane for agent-driven development workflows. +Gito는 agent-driven development를 위한 platformless Git control plane이다. +여러 Git repository를 등록하고, workspace lease, Git operation, agent-shell, +IOP 실행, provider adapter, normalized event를 하나의 제어 흐름으로 묶는다. -It manages registered repositories, workspace leases, Git operations, agent-shell -execution, provider adapters, and normalized events. Git itself stays -platform-neutral; GitHub, GitLab, Gitea, Plane, Jira, and similar systems are -integrated through adapters. +Git 자체 작업은 GitHub/GitLab/Gitea 같은 플랫폼을 몰라도 동작해야 한다. +PR/MR, review, checks, webhook 같은 플랫폼 기능은 provider adapter 뒤에 둔다. -## Direction +## 현재 상태 -- Server: Go -- Control surface: Flutter app and `gitoctl` in future slices -- State: PostgreSQL as source of truth -- Event acceleration: Redis optional, not required for the first slice -- Internal transport: proto-socket first -- REST: health/readiness, smoke/curl, provider callbacks, simple admin bootstrap -- gRPC: intentionally excluded from the initial design +초기 scaffold 단계다. Go core는 build/test 가능한 최소 골격을 갖고 있고, +Flutter client는 control surface 경계만 잡혀 있다. -## Runtime Shape +현재 가능한 범위: + +- Go `server`, `worker`, `shell` command build +- health/readiness REST placeholder +- platformless `gitengine` 최소 테스트 +- core domain DTO 후보 +- provider-neutral `ChangeRequest` adapter 경계 +- Postgres migration 초기안 +- `agent-ops` 규칙/스킬 기반 작업 진입점 + +아직 구현 전인 범위: + +- 실제 proto-socket channel/action +- 실제 PostgreSQL store wiring +- agent-shell outbound 연결과 command streaming +- IOP CLI 실행 orchestration +- GitHub/GitLab/Gitea provider adapter +- Flutter 화면 구성과 operation event 구독 + +## 빠른 시작 + +```sh +bin/test +bin/lint +bin/build +``` + +빌드가 끝나면 root `bin/` 아래에 아래 실행 파일이 생성된다. + +```text +bin/gito-server +bin/gito-worker +bin/gito-shell +``` + +서버 scaffold 실행: + +```sh +HTTP_ADDR=:8080 bin/gito-server +``` + +확인: + +```sh +curl http://127.0.0.1:8080/healthz +curl http://127.0.0.1:8080/readyz +``` + +## 주요 명령 + +| 목적 | 명령 | 비고 | +| --- | --- | --- | +| 전체 테스트 | `bin/test` | Go test 실행, Flutter test scaffold가 없으면 client test는 skip | +| lint | `bin/lint` | `go vet ./...`, Flutter가 있으면 `flutter analyze --no-fatal-infos` | +| build | `bin/build` | `gito-server`, `gito-worker`, `gito-shell` 생성 | +| Core test | `cd services/core && go test ./...` | Go module 기준 직접 실행 | +| Core lint | `cd services/core && go vet ./...` | Go vet 직접 실행 | + +## 구조 + +| 경로 | 역할 | +| --- | --- | +| `services/core/cmd/server/` | control plane server entrypoint | +| `services/core/cmd/worker/` | operation worker entrypoint | +| `services/core/cmd/shell/` | agent-shell runner entrypoint | +| `services/core/internal/controlplane/` | REST/proto-socket surface, auth/policy gate 후보 | +| `services/core/internal/core/` | provider-neutral domain model | +| `services/core/internal/gitengine/` | platformless Git operation layer | +| `services/core/internal/worker/` | operation 실행 orchestration 후보 | +| `services/core/internal/agentshell/` | local command/IOP execution boundary | +| `services/core/internal/provider/` | GitHub/GitLab/Gitea/Plane/Jira adapter boundary | +| `services/core/internal/events/` | normalized event model | +| `services/core/internal/storage/` | PostgreSQL store boundary | +| `services/core/migrations/` | database schema migration | +| `apps/client/` | Flutter control surface scaffold | +| `packages/contracts/` | proto-socket, REST, event, DTO contract notes | +| `docs/` | 사람용 architecture/operation 문서 | +| `agent-ops/` | agent rules, skills, roadmap helper | +| `agent-test/` | local validation rules | + +## 설계 방향 + +Gito는 처음에는 modular monolith로 시작하되, 실행 역할은 분리 가능하게 둔다. ```text Flutter UI / gitoctl @@ -33,27 +110,62 @@ Agent Shell / IOP / Git Engine Provider Adapters ``` -## Repository Layout +원칙: -```text -apps/client/ Flutter control surface scaffold -services/core/ Go control plane, worker, shell commands -packages/contracts/ proto-socket, REST, event, and provider contract notes -docs/ Architecture and operation notes -bin/ Root test/lint/build helpers -agent-ops/ Agent operating rules -agent-test/ Local validation rules -``` +- `controlplane`은 요청, 정책, 관찰, event stream을 소유한다. +- `core`는 repo, workspace lease, operation, revision cursor, event 원장을 소유한다. +- `worker`는 operation 실행을 조율한다. +- `agentshell`은 실제 workspace command, Git CLI, IOP CLI 실행을 맡는다. +- `gitengine`은 platformless여야 하며 local bare repo만으로 테스트 가능해야 한다. +- `provider`는 PR/MR/review/checks/webhook 같은 플랫폼 기능만 맡는다. +- PostgreSQL은 source of truth이고, Redis는 필요할 때 fanout/stream 가속 계층으로만 둔다. -## First Commands +## 통신 정책 -```sh -bin/test -bin/lint -bin/build -``` +- 내부 runtime 통신은 proto-socket을 우선한다. +- REST는 health/readiness, provider callback, smoke/curl, 단순 bootstrap에만 둔다. +- gRPC는 초기 설계에서 제외한다. +- webhook은 빠른 wakeup signal로만 보고, 최종 판정은 Git revision/diff 또는 provider read API로 재검증한다. -The initial Go scaffold is intentionally small but buildable. Flutter platform -directories are not generated yet; `apps/client` currently records the control -surface boundary and a minimal Dart entry point. +## 작업 맥락 +작업을 시작할 때는 먼저 [AGENTS.md](AGENTS.md)를 읽고, 변경 경로에 맞는 domain rule을 확인한다. + +주요 문서: + +- [Project rules](agent-ops/rules/project/rules.md) +- [Core domain rules](agent-ops/rules/project/domain/core/rules.md) +- [Client domain rules](agent-ops/rules/project/domain/client/rules.md) +- [Contracts domain rules](agent-ops/rules/project/domain/contracts/rules.md) +- [Workspace ops rules](agent-ops/rules/project/domain/workspace-ops/rules.md) +- [Local test rules](agent-test/local/rules.md) +- [Architecture](docs/architecture.md) +- [Control plane contract candidates](packages/contracts/notes/gito-control-plane.md) + +## 개발 흐름 + +1. 변경 전에 관련 domain rule을 읽는다. +2. core 변경은 `cd services/core && go test ./...` 또는 `bin/test`로 확인한다. +3. contracts 변경은 README/notes의 transport 정책과 맞는지 확인한다. +4. client 변경은 Flutter toolchain이 있으면 `flutter analyze --no-fatal-infos`와 `flutter test`를 실행한다. +5. 생성된 binary, `.dart_tool/`, local secret 파일은 commit하지 않는다. + +## 환경 변수 + +| 이름 | 설명 | 필수 | +| --- | --- | --- | +| `APP_ENV` | runtime 환경 이름, 기본값 `local` | 아니오 | +| `HTTP_ADDR` | server listen address, 기본값 `:8080` | 아니오 | +| `DATABASE_URL` | PostgreSQL connection string | 실제 storage wiring 이후 필요 | +| `REDIS_URL` | optional Redis endpoint | 아니오 | +| `PROTO_SOCKET_PATH` | proto-socket endpoint path, 기본값 `/proto-socket` | 아니오 | +| `WORKER_ENABLED` | worker 실행 여부, 기본값 `true` | 아니오 | +| `GITO_SHELL_ID` | agent-shell 식별자, 기본값 `local-shell` | 아니오 | + +Credential 값은 tracked 파일에 쓰지 않는다. 필요한 경우 `credential_ref`나 ignored local secret file로 연결한다. + +## 참고 문서 + +- [docs/architecture.md](docs/architecture.md) +- [packages/contracts/notes/gito-control-plane.md](packages/contracts/notes/gito-control-plane.md) +- [agent-ops/skills/common/create-readme/SKILL.md](agent-ops/skills/common/create-readme/SKILL.md) diff --git a/agent-ops/.version b/agent-ops/.version new file mode 100644 index 0000000..5c7cd93 --- /dev/null +++ b/agent-ops/.version @@ -0,0 +1 @@ +1.1.136 diff --git a/agent-ops/bin/ai-ignore.sh b/agent-ops/bin/ai-ignore.sh new file mode 100755 index 0000000..964ba95 --- /dev/null +++ b/agent-ops/bin/ai-ignore.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash + +# Shared AI ignore / permission defaults for init and sync flows. +AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN="agent-task/archive/**" +AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN="agent-roadmap/archive/**" +AGENT_ROADMAP_CURRENT_LOCAL_PATTERN="agent-roadmap/current.md" +AGENT_OPS_AI_IGNORE_BLOCK_BEGIN="# BEGIN Agent-Ops managed ignore" +AGENT_OPS_AI_IGNORE_BLOCK_END="# END Agent-Ops managed ignore" +AGENT_OPS_GITIGNORE_BLOCK_BEGIN="# BEGIN Agent-Ops managed gitignore" +AGENT_OPS_GITIGNORE_BLOCK_END="# END Agent-Ops managed gitignore" +AGENT_OPS_AI_IGNORE_FILES=(".geminiignore" ".aiexclude" ".cursorignore" ".clineignore") +AGENT_OPS_AI_PERMISSION_FILES=(".claude/settings.json" "opencode.json") +AGENT_OPS_AI_SYNC_FILES=(".gitignore" "${AGENT_OPS_AI_IGNORE_FILES[@]}" "${AGENT_OPS_AI_PERMISSION_FILES[@]}") + +agent_ops_ensure_gitignore_task_artifact_block() { + local file="$1" + local tmp + + touch "$file" + if grep -qxF "$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" "$file" \ + && grep -qxF "$AGENT_OPS_GITIGNORE_BLOCK_END" "$file"; then + tmp="$(mktemp "$file.tmp.XXXXXX")" + awk \ + -v begin="$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" \ + -v end="$AGENT_OPS_GITIGNORE_BLOCK_END" \ + -v current="$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" ' + $0 == begin { + print begin + print "!agent-task/" + print "!agent-task/**/" + print "!agent-task/**/*.md" + print "!agent-task/**/*.log" + print current + print end + in_block = 1 + next + } + $0 == end && in_block { + in_block = 0 + next + } + !in_block { print } + ' "$file" > "$tmp" + mv "$tmp" "$file" + else + if [[ -s "$file" ]]; then + printf "\n" >> "$file" + fi + printf "%s\n" "$AGENT_OPS_GITIGNORE_BLOCK_BEGIN" >> "$file" + printf "%s\n" "!agent-task/" >> "$file" + printf "%s\n" "!agent-task/**/" >> "$file" + printf "%s\n" "!agent-task/**/*.md" >> "$file" + printf "%s\n" "!agent-task/**/*.log" >> "$file" + printf "%s\n" "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" >> "$file" + printf "%s\n" "$AGENT_OPS_GITIGNORE_BLOCK_END" >> "$file" + fi +} + +agent_ops_ensure_ai_ignore_block() { + local file="$1" + local tmp + + touch "$file" + if grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" "$file" \ + && grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_END" "$file"; then + tmp="$(mktemp "$file.tmp.XXXXXX")" + awk \ + -v begin="$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" \ + -v end="$AGENT_OPS_AI_IGNORE_BLOCK_END" \ + -v task="$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" \ + -v roadmap="$AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN" ' + $0 == begin { + print begin + print task + print roadmap + print end + in_block = 1 + next + } + $0 == end && in_block { + in_block = 0 + next + } + !in_block { print } + ' "$file" > "$tmp" + mv "$tmp" "$file" + else + if [[ -s "$file" ]]; then + printf "\n" >> "$file" + fi + printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" >> "$file" + printf "%s\n" "$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" >> "$file" + printf "%s\n" "$AGENT_ROADMAP_ARCHIVE_IGNORE_PATTERN" >> "$file" + printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_END" >> "$file" + fi +} + +agent_ops_merge_json_with_jq() { + local file="$1" + local filter="$2" + local fallback_note="$3" + local tmp + + if ! command -v jq >/dev/null 2>&1; then + echo "$fallback_note" + return + fi + + tmp="$(mktemp "$file.tmp.XXXXXX")" + if jq "$filter" "$file" > "$tmp"; then + mv "$tmp" "$file" + else + rm -f "$tmp" + echo "$fallback_note" + fi +} + +agent_ops_merge_claude_settings() { + local file="$1" + local filter=' +def append_unique($item): + if index($item) then . else . + [$item] end; +def as_array: + if type == "array" then . elif . == null then [] else [.] end; +def as_object: + if type == "object" then . else {} end; + +.permissions = ((.permissions // {}) | as_object) +| .permissions.deny = ( + (.permissions.deny | as_array) + | map(select(. != "Read(./agent-roadmap/archive/**)")) + | append_unique("Read(./agent-task/archive/**)") + ) +' + + agent_ops_merge_json_with_jq \ + "$file" \ + "$filter" \ + "Note: .claude/settings.json exists; add Read(./agent-task/archive/**) and remove Read(./agent-roadmap/archive/**) manually." +} + +agent_ops_claude_settings_complete() { + local file="$1" + local filter=' +def as_array: + if type == "array" then . elif . == null then [] else [.] end; + +(.permissions.deny | as_array) +as $deny +| (($deny | index("Read(./agent-task/archive/**)")) != null) +and (($deny | index("Read(./agent-roadmap/archive/**)")) == null) +' + + if command -v jq >/dev/null 2>&1; then + jq -e "$filter" "$file" >/dev/null 2>&1 + else + grep -q "agent-task/archive" "$file" + fi +} + +agent_ops_merge_opencode_config() { + local file="$1" + local filter=' +def append_unique($item): + if index($item) then . else . + [$item] end; +def as_array: + if type == "array" then . elif . == null then [] else [.] end; +def as_object: + if type == "object" then . else {} end; + +.permission = ((.permission // {}) | as_object) +| .permission.read = ((.permission.read // {}) | as_object) +| .permission.read["agent-task/archive/**"] = "deny" +| del(.permission.read["agent-roadmap/archive/**"]) +| .permission.glob = ((.permission.glob // {}) | as_object) +| .permission.glob["agent-task/archive/**"] = "deny" +| del(.permission.glob["agent-roadmap/archive/**"]) +| .watcher = ((.watcher // {}) | as_object) +| .watcher.ignore = ( + (.watcher.ignore | as_array) + | append_unique("agent-task/archive/**") + | append_unique("agent-roadmap/archive/**") + ) +' + + agent_ops_merge_json_with_jq \ + "$file" \ + "$filter" \ + "Note: opencode.json exists; add agent-task/archive/** read/glob deny, remove agent-roadmap/archive/** read/glob hard deny, and add agent-task/archive/** plus agent-roadmap/archive/** watcher ignore manually." +} + +agent_ops_opencode_config_complete() { + local file="$1" + local filter=' +def as_array: + if type == "array" then . elif . == null then [] else [.] end; + +(.permission.read["agent-task/archive/**"] == "deny") +and (.permission.glob["agent-task/archive/**"] == "deny") +and (.permission.read["agent-roadmap/archive/**"] != "deny") +and (.permission.glob["agent-roadmap/archive/**"] != "deny") +and ((.watcher.ignore | as_array) as $ignore | (($ignore | index("agent-task/archive/**")) != null) and (($ignore | index("agent-roadmap/archive/**")) != null)) +' + + if command -v jq >/dev/null 2>&1; then + jq -e "$filter" "$file" >/dev/null 2>&1 + else + grep -q "agent-task/archive" "$file" && grep -q "agent-roadmap/archive" "$file" + fi +} + +ensure_agent_ops_ai_ignore_config() { + local target_dir="$1" + local ignore_file + + if [[ -f "$target_dir/.agent-ops-source" ]]; then + echo " AI ignore 보강 건너뜀: .agent-ops-source repo" + return + fi + + agent_ops_ensure_gitignore_task_artifact_block "$target_dir/.gitignore" + + for ignore_file in "${AGENT_OPS_AI_IGNORE_FILES[@]}"; do + agent_ops_ensure_ai_ignore_block "$target_dir/$ignore_file" + done + + mkdir -p "$target_dir/.claude" + if [[ ! -f "$target_dir/.claude/settings.json" ]]; then + cat > "$target_dir/.claude/settings.json" <<'EOF' +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "deny": [ + "Read(./agent-task/archive/**)" + ] + } +} +EOF + elif ! agent_ops_claude_settings_complete "$target_dir/.claude/settings.json"; then + agent_ops_merge_claude_settings "$target_dir/.claude/settings.json" + fi + + if [[ ! -f "$target_dir/opencode.json" ]]; then + cat > "$target_dir/opencode.json" <<'EOF' +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "read": { + "agent-task/archive/**": "deny" + }, + "glob": { + "agent-task/archive/**": "deny" + } + }, + "watcher": { + "ignore": [ + "agent-task/archive/**", + "agent-roadmap/archive/**" + ] + } +} +EOF + elif ! agent_ops_opencode_config_complete "$target_dir/opencode.json"; then + agent_ops_merge_opencode_config "$target_dir/opencode.json" + fi + + echo " AI permission 표준 적용: roadmap archive hard deny 제거" +} diff --git a/agent-ops/bin/bump-version.sh b/agent-ops/bin/bump-version.sh new file mode 100755 index 0000000..4cc50ef --- /dev/null +++ b/agent-ops/bin/bump-version.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# bump-version.sh +# patch +1, 999 초과 시 minor 올림. major는 수동 관리. +# 결과 버전을 stdout으로 출력. + +set -euo pipefail + +YELLOW='\033[1;33m'; RESET='\033[0m' + +VERSION="${1:-}" +if [[ -z "$VERSION" ]]; then + echo "사용법: $0 " >&2 + exit 1 +fi + +IFS='.' read -r major minor patch <<< "$VERSION" + +patch=$((patch + 1)) +if [[ $patch -gt 999 ]]; then + patch=0 + minor=$((minor + 1)) +fi +if [[ $minor -gt 999 ]]; then + minor=0 + echo -e "${YELLOW}⚠ minor 버전이 999를 초과했습니다. major 버전을 수동으로 올려주세요.${RESET}" >&2 +fi + +echo "$major.$minor.$patch" diff --git a/agent-ops/bin/entry-files.sh b/agent-ops/bin/entry-files.sh new file mode 100644 index 0000000..dfb6f90 --- /dev/null +++ b/agent-ops/bin/entry-files.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Shared entry-point file list for init/sync scripts. +AGENT_OPS_ENTRY_FILES=("GEMINI.md" "CLAUDE.md" "AGENTS.md" ".cursorrules" ".clinerules") + +apply_agent_ops_entry_files() { + local rules_md="$1" + local target_root="$2" + + if [[ ! -f "$rules_md" ]]; then + if [[ -n "${YELLOW:-}" && -n "${RESET:-}" ]]; then + echo -e "${YELLOW} rules.md 없음, 진입 파일 재적용 건너뜀${RESET}" + else + echo " rules.md 없음, 진입 파일 재적용 건너뜀" + fi + return + fi + + local f + for f in "${AGENT_OPS_ENTRY_FILES[@]}"; do + cp "$rules_md" "$target_root/$f" + echo " 진입 파일 적용: $f" + done +} diff --git a/agent-ops/bin/init-agent-ops.sh b/agent-ops/bin/init-agent-ops.sh new file mode 100755 index 0000000..765683a --- /dev/null +++ b/agent-ops/bin/init-agent-ops.sh @@ -0,0 +1,343 @@ +#!/bin/bash + +# agent-ops/bin/init-agent-ops.sh +# 프로젝트에 agent-ops 스캐폴드를 초기화하는 스크립트 + +set -e + +SCRIPT_DIR=$(realpath "$(dirname "$0")") +SOURCE_DIR=$(realpath "$SCRIPT_DIR/..") +source "$SCRIPT_DIR/entry-files.sh" +source "$SCRIPT_DIR/ai-ignore.sh" + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +TARGET_DIR=$(realpath "$1") + +create_project_agent_ops_dirs() { + local agent_ops_dir="$1" + + mkdir -p "$agent_ops_dir/rules/project/domain" + mkdir -p "$agent_ops_dir/rules/private" + mkdir -p "$agent_ops_dir/skills/project" +} + +copy_common_agent_ops() { + local source_dir="$1" + local target_agent_ops_dir="$2" + + mkdir -p "$target_agent_ops_dir/rules" + mkdir -p "$target_agent_ops_dir/skills" + + cp "$source_dir/.version" "$target_agent_ops_dir/" + rm -rf "$target_agent_ops_dir/bin" + rm -rf "$target_agent_ops_dir/rules/common" + rm -rf "$target_agent_ops_dir/skills/common" + cp -r "$source_dir/bin" "$target_agent_ops_dir/" + cp -r "$source_dir/rules/common" "$target_agent_ops_dir/rules/" + cp -r "$source_dir/skills/common" "$target_agent_ops_dir/skills/" +} + +ensure_common_rules_file() { + local source_dir="$1" + local target_agent_ops_dir="$2" + local source_rules="$source_dir/rules/common/rules.md" + local target_rules="$target_agent_ops_dir/rules/common/rules.md" + + if [ ! -f "$source_rules" ]; then + echo "Error: common rules file not found: $source_rules" >&2 + exit 1 + fi + + mkdir -p "$(dirname "$target_rules")" + cp "$source_rules" "$target_rules" +} + +ensure_gitignore_entry() { + local gitignore_file="$1" + local entry="$2" + + if ! grep -qxF "$entry" "$gitignore_file"; then + printf '%s\n' "$entry" >> "$gitignore_file" + fi +} + +discover_agent_test_domains() { + local target_dir="$1" + local domain_root="$target_dir/agent-ops/rules/project/domain" + local project_rules="$target_dir/agent-ops/rules/project/rules.md" + + if [ -d "$domain_root" ]; then + find "$domain_root" -mindepth 2 -maxdepth 2 -name rules.md -print \ + | while IFS= read -r rule_file; do + basename "$(dirname "$rule_file")" + done + fi + + if [ -f "$project_rules" ]; then + grep -Eo 'agent-ops/rules/project/domain/[^/`[:space:]]+/rules\.md' "$project_rules" \ + | awk -F/ '{ print $(NF-1) }' + fi +} + +ensure_agent_test_route() { + local rules_file="$1" + local route_line="$2" + local placeholder="- 현재 등록된 도메인/검증 시나리오별 테스트 문서 없음." + local tmp + + if grep -qxF -- "$route_line" "$rules_file"; then + return + fi + + tmp="$(mktemp "$rules_file.tmp.XXXXXX")" + if grep -qxF -- "$placeholder" "$rules_file"; then + awk -v placeholder="$placeholder" -v route_line="$route_line" ' + $0 == placeholder { print route_line; next } + { print } + ' "$rules_file" > "$tmp" + else + awk -v route_line="$route_line" ' + BEGIN { added = 0 } + { print } + !added && $0 == "## 라우팅" { + print "" + print route_line + added = 1 + } + END { + if (!added) { + print "" + print "## 라우팅" + print "" + print route_line + } + } + ' "$rules_file" > "$tmp" + fi + mv "$tmp" "$rules_file" +} + +ensure_agent_test_profile() { + local target_dir="$1" + local env="$2" + local profile="$3" + local domain="$4" + local verification_type="$5" + local scope="$6" + local today="$7" + local profile_file="$target_dir/agent-test/$env/$profile.md" + local rules_file="$target_dir/agent-test/$env/rules.md" + local route_line + + mkdir -p "$(dirname "$profile_file")" + + if [ ! -f "$profile_file" ]; then + cat > "$profile_file" < + +## 분류 + +- domain: $domain +- verification_type: $verification_type +- scope: $scope + +## 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 명령 + +- setup: +- lint: +- unit: +- smoke: +- e2e: +- model: +- full-cycle: + +## 필수 검증 + +- <확인 필요> + +## 보조 검증 + +- <확인 필요> + +## 판정 기준 + +- <확인 필요> + +## 기준 출력 예시 + +\`\`\`text +<필요한 경우 기대 출력 예시> +\`\`\` + +## 차단 기준 + +- <확인 필요> + +## 보고 항목 + +- 실행한 명령: +- 성공한 검증: +- 실패/차단된 검증: +- 생략 사유: +- 남은 위험: + +## 금지 사항 + +- 확인되지 않은 host, port, token, endpoint를 추측해 쓰지 않는다. +- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다. +EOF + fi + + route_line="- $domain / $verification_type / $scope: \`agent-test/$env/$profile.md\`" + ensure_agent_test_route "$rules_file" "$route_line" +} + +ensure_agent_test_local() { + local target_dir="$1" + local local_rules="$target_dir/agent-test/local/rules.md" + local today + local domain + local created_domain_profile="0" + + today="$(date +%F)" + mkdir -p "$(dirname "$local_rules")" + mkdir -p "$target_dir/agent-test/runs" + + if [ ! -f "$local_rules" ]; then + cat > "$local_rules" <-smoke 문서를 기본 baseline으로 둔다. +- 도메인이 아직 없을 때만 project-smoke를 fallback baseline으로 둔다. +- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다. +EOF + fi + + while IFS= read -r domain; do + [ -n "$domain" ] || continue + created_domain_profile="1" + ensure_agent_test_profile \ + "$target_dir" \ + "local" \ + "$domain-smoke" \ + "$domain" \ + "smoke" \ + "도메인 기본 smoke 검증" \ + "$today" + done < <(discover_agent_test_domains "$target_dir" | sort -u) + + if [ "$created_domain_profile" = "0" ]; then + ensure_agent_test_profile \ + "$target_dir" \ + "local" \ + "project-smoke" \ + "project-wide" \ + "smoke" \ + "프로젝트 기본 smoke 검증" \ + "$today" + fi +} + +echo "Initializing agent-ops in: $TARGET_DIR" +echo "Source agent-ops: $SOURCE_DIR" + +# 1. 대상 폴더 생성 (프로젝트 전용 폴더는 내용 복사 없이 폴더만 생성) +create_project_agent_ops_dirs "$TARGET_DIR/agent-ops" + +# 2. 공통 요소 복사 (프로젝트 전용 설정은 제외) +copy_common_agent_ops "$SOURCE_DIR" "$TARGET_DIR/agent-ops" + +# 3. 에이전트 진입 파일 생성 (common/rules.md 복사) +COMMON_RULES="$SOURCE_DIR/rules/common/rules.md" +apply_agent_ops_entry_files "$COMMON_RULES" "$TARGET_DIR" +ensure_common_rules_file "$SOURCE_DIR" "$TARGET_DIR/agent-ops" + +# 4. AI ignore / permission 설정 +ensure_agent_ops_ai_ignore_config "$TARGET_DIR" + +# 5. .gitignore 설정 +TOUCH_GITIGNORE="$TARGET_DIR/.gitignore" +touch "$TOUCH_GITIGNORE" +agent_ops_ensure_gitignore_task_artifact_block "$TOUCH_GITIGNORE" +if ! grep -q "agent-ops/rules/private/" "$TOUCH_GITIGNORE"; then + echo "" >> "$TOUCH_GITIGNORE" + echo "# Agent-Ops Private Rules" >> "$TOUCH_GITIGNORE" + echo "agent-ops/rules/private/" >> "$TOUCH_GITIGNORE" +fi +if ! grep -qxF "# Agent-Test Local Environment" "$TOUCH_GITIGNORE"; then + echo "" >> "$TOUCH_GITIGNORE" + echo "# Agent-Test Local Environment" >> "$TOUCH_GITIGNORE" +fi +ensure_gitignore_entry "$TOUCH_GITIGNORE" "agent-test/local/" +ensure_gitignore_entry "$TOUCH_GITIGNORE" "agent-test/runs/" + +# 6. local 테스트 환경 생성 +ensure_agent_test_local "$TARGET_DIR" + +echo "Successfully initialized agent-ops in $TARGET_DIR" +echo "Note: agent-ops/rules/project and agent-ops/skills/project are initialized as empty." +echo "Note: agent-test/local rules and baseline test profiles are initialized and ignored by git." diff --git a/agent-ops/bin/roadmap-dependency-checker.sh b/agent-ops/bin/roadmap-dependency-checker.sh new file mode 100755 index 0000000..71a3dfc --- /dev/null +++ b/agent-ops/bin/roadmap-dependency-checker.sh @@ -0,0 +1,370 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENT_OPS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PROJECT_ROOT="$(cd "$AGENT_OPS_DIR/.." && pwd)" +WORKSPACE_ROOT="$(cd "$PROJECT_ROOT/.." && pwd)" +LOCKS_FILE="${AGENT_ROADMAP_LOCKS_FILE:-$WORKSPACE_ROOT/.agent-roadmap-sync/locks.yaml}" + +usage() { + cat <<'EOF' +Usage: roadmap-dependency-checker.sh [locks.yaml] + roadmap-dependency-checker.sh --find-milestone [locked|rely-on|both] [locks.yaml] + +Validates locked, rely-on.target, and rely-on.status for . +Prints true when every rely-on.status is enable. +Prints false when at least one rely-on.status is disable. +With --find-milestone, prints matching lock ids as locked: or rely-on:, +or none when the milestone identity is not referenced. + +Exit codes: + 0 all dependencies are enabled + 1 at least one dependency is still disabled + 2 configuration, input, or parse error +For --find-milestone, exit code 0 means the lookup completed; stdout is either +matching entries or none. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +MODE="check" +LOCK_ID="" +FIND_IDENTITY="" +FIND_DIRECTION="both" + +if [[ "${1:-}" == "--find-milestone" ]]; then + MODE="find" + if [[ $# -lt 2 || $# -gt 4 ]]; then + usage >&2 + exit 2 + fi + FIND_IDENTITY="$2" + if [[ -z "$FIND_IDENTITY" ]]; then + echo "error: milestone identity is required" >&2 + exit 2 + fi + if [[ $# -ge 3 ]]; then + case "$3" in + locked|rely-on|both) + FIND_DIRECTION="$3" + if [[ $# -eq 4 ]]; then + LOCKS_FILE="$4" + fi + ;; + *) + if [[ $# -eq 3 ]]; then + LOCKS_FILE="$3" + else + usage >&2 + exit 2 + fi + ;; + esac + fi +elif [[ $# -lt 1 || $# -gt 2 ]]; then + usage >&2 + exit 2 +else + LOCK_ID="$1" + if [[ -n "${2:-}" ]]; then + LOCKS_FILE="$2" + fi +fi + +if [[ "$MODE" == "check" && -z "$LOCK_ID" ]]; then + echo "error: lock id is required" >&2 + exit 2 +fi + +if [[ ! -r "$LOCKS_FILE" ]]; then + echo "error: locks file not readable: $LOCKS_FILE" >&2 + exit 2 +fi + +if [[ "$MODE" == "find" ]]; then + awk -v identity="$FIND_IDENTITY" -v direction="$FIND_DIRECTION" ' +function trim(value) { + gsub(/\r/, "", value) + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value +} + +function unquote(value) { + value = trim(value) + sub(/[[:space:]]+#.*$/, "", value) + value = trim(value) + if (value ~ /^".*"$/ || value ~ /^'\''.*'\''$/) { + return substr(value, 2, length(value) - 2) + } + return trim(value) +} + +function reset_entry() { + entry_id = "" + locked = "" + in_rely_on = 0 + rely_count = 0 + delete rely_targets +} + +function finish_entry( i) { + if (entry_id == "") { + return + } + if ((direction == "both" || direction == "locked") && locked == identity) { + print "locked:" entry_id + found = 1 + } + if (direction == "both" || direction == "rely-on") { + for (i = 1; i <= rely_count; i++) { + if (rely_targets[i] == identity) { + print "rely-on:" entry_id + found = 1 + break + } + } + } +} + +BEGIN { + found = 0 + reset_entry() +} + +/^[[:space:]]*($|#)/ { + next +} + +/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/ { + finish_entry() + reset_entry() + value = $0 + sub(/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/, "", value) + entry_id = unquote(value) + next +} + +entry_id != "" && /^[[:space:]]*locked:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*locked:[[:space:]]*/, "", value) + locked = unquote(value) + next +} + +entry_id != "" && /^[[:space:]]*rely-on:[[:space:]]*/ { + in_rely_on = 1 + next +} + +entry_id != "" && in_rely_on && /^[[:space:]]*-[[:space:]]*target:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*-[[:space:]]*target:[[:space:]]*/, "", value) + rely_targets[++rely_count] = unquote(value) + next +} + +entry_id != "" && in_rely_on && /^[[:space:]]*target:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*target:[[:space:]]*/, "", value) + rely_targets[++rely_count] = unquote(value) + next +} + +END { + finish_entry() + if (!found) { + print "none" + } +} +' "$LOCKS_FILE" + exit 0 +fi + +awk -v wanted="$LOCK_ID" ' +function trim(value) { + gsub(/\r/, "", value) + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value +} + +function unquote(value) { + value = trim(value) + sub(/[[:space:]]+#.*$/, "", value) + value = trim(value) + if (value ~ /^".*"$/ || value ~ /^'\''.*'\''$/) { + return substr(value, 2, length(value) - 2) + } + return trim(value) +} + +function fail(message) { + error = message + printf "error: %s\n", error > "/dev/stderr" + exit 2 +} + +function finish_dependency() { + if (!selected || !in_dependency) { + return + } + if (!dependency_has_target) { + fail("missing rely-on.target for lock id: " wanted) + } + if (!dependency_has_status) { + fail("missing rely-on.status for lock id: " wanted) + } + in_dependency = 0 +} + +function finish_entry() { + if (!selected) { + return + } + finish_dependency() + if (!entry_has_locked) { + fail("missing locked for lock id: " wanted) + } + if (total == 0) { + fail("no rely-on.status entries for lock id: " wanted) + } + selected = 0 +} + +BEGIN { + selected = 0 + matches = 0 + total = 0 + enabled = 0 + error = "" + entry_has_locked = 0 + in_rely_on = 0 + in_dependency = 0 + dependency_has_target = 0 + dependency_has_status = 0 +} + +/^[[:space:]]*($|#)/ { + next +} + +/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/ { + finish_entry() + value = $0 + sub(/^[[:space:]]*-[[:space:]]*id:[[:space:]]*/, "", value) + id = unquote(value) + if (id == wanted) { + if (matches > 0) { + fail("duplicate lock id: " wanted) + } + selected = 1 + matches++ + total = 0 + enabled = 0 + entry_has_locked = 0 + in_rely_on = 0 + in_dependency = 0 + dependency_has_target = 0 + dependency_has_status = 0 + } + next +} + +selected && /^[[:space:]]*locked:[[:space:]]*/ { + value = $0 + sub(/^[[:space:]]*locked:[[:space:]]*/, "", value) + locked = unquote(value) + if (locked == "") { + fail("empty locked for lock id: " wanted) + } + entry_has_locked = 1 + next +} + +selected && /^[[:space:]]*rely-on:[[:space:]]*/ { + in_rely_on = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*-[[:space:]]*target:[[:space:]]*/ { + finish_dependency() + in_dependency = 1 + dependency_has_target = 0 + dependency_has_status = 0 + value = $0 + sub(/^[[:space:]]*-[[:space:]]*target:[[:space:]]*/, "", value) + target = unquote(value) + if (target == "") { + fail("empty rely-on.target for lock id: " wanted) + } + dependency_has_target = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*target:[[:space:]]*/ { + if (!in_dependency) { + fail("rely-on.target outside dependency item for lock id: " wanted) + } + value = $0 + sub(/^[[:space:]]*target:[[:space:]]*/, "", value) + target = unquote(value) + if (target == "") { + fail("empty rely-on.target for lock id: " wanted) + } + dependency_has_target = 1 + next +} + +selected && in_rely_on && /^[[:space:]]*status:[[:space:]]*/ { + if (!in_dependency) { + fail("rely-on.status outside dependency item for lock id: " wanted) + } + value = $0 + sub(/^[[:space:]]*status:[[:space:]]*/, "", value) + status = unquote(value) + if (status != "enable" && status != "disable") { + fail("invalid rely-on.status for " wanted ": " status) + } + if (dependency_has_status) { + fail("duplicate rely-on.status for lock id: " wanted) + } + dependency_has_status = 1 + total++ + if (status == "enable") { + enabled++ + } + next +} + +selected && in_rely_on && /^[[:space:]]*-[[:space:]]*/ { + finish_dependency() + fail("missing rely-on.target for lock id: " wanted) +} + +selected && /^[[:space:]]*status:[[:space:]]*/ { + fail("status outside rely-on for lock id: " wanted) +} + +END { + if (error != "") { + exit 2 + } + if (matches == 0) { + printf "error: lock id not found: %s\n", wanted > "/dev/stderr" + exit 2 + } + finish_entry() + if (total == enabled) { + print "true" + exit 0 + } + print "false" + exit 1 +} +' "$LOCKS_FILE" diff --git a/agent-ops/bin/sync.sh b/agent-ops/bin/sync.sh new file mode 100755 index 0000000..21b7eca --- /dev/null +++ b/agent-ops/bin/sync.sh @@ -0,0 +1,338 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ── 경로 설정 ──────────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENT_OPS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PROJECT_ROOT="$(cd "$AGENT_OPS_DIR/.." && pwd)" + +# ── 색상 ───────────────────────────────────────────────────────────────────── +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; RESET='\033[0m' + +# ── 진입 파일 공통 관리 ────────────────────────────────────────────────────── +source "$SCRIPT_DIR/entry-files.sh" +source "$SCRIPT_DIR/ai-ignore.sh" + +# ── 대상 경로 해석 (폴더명 / 상대경로 / 절대경로) ──────────────────────────── +resolve_target() { + local input="$1" + if [[ "$input" == /* ]]; then + [[ -d "$input" ]] && echo "$input" || echo "" + return + fi + if [[ "$input" == */* ]]; then + local resolved + resolved="$(cd "$input" 2>/dev/null && pwd)" || { echo ""; return; } + echo "$resolved" + return + fi + local sibling="$(dirname "$PROJECT_ROOT")/$input" + [[ -d "$sibling" ]] && echo "$sibling" || echo "" +} + +# ── 버전 비교: v1 > v2 이면 0 반환 ─────────────────────────────────────────── +version_gt() { + local v1="$1" v2="$2" + [[ "$v1" == "$v2" ]] && return 1 + [[ "$(printf '%s\n%s' "$v1" "$v2" | sort -V | head -1)" == "$v2" ]] +} + +# ── 버전 +1 ────────────────────────────────────────────────────────────────── +bump_version() { + bash "$SCRIPT_DIR/bump-version.sh" "$1" +} + +# ── 폴더 동기화 (삭제된 파일도 반영) ──────────────────────────────────────── +sync_folder() { + local src="$1" dst="$2" exclude="${3:-}" + mkdir -p "$dst" + # dst에서 src에 없는 항목 제거 (exclude 파일 보존) + find "$dst" -mindepth 1 -maxdepth 1 | while IFS= read -r item; do + local name + name="$(basename "$item")" + [[ -n "$exclude" && "$name" == "$exclude" ]] && continue + if [[ ! -e "$src/$name" ]]; then + rm -rf "$item" + fi + done + # src에서 dst로 복사 (exclude 파일 제외) + find "$src" -mindepth 1 -maxdepth 1 | while IFS= read -r item; do + local name + name="$(basename "$item")" + [[ -n "$exclude" && "$name" == "$exclude" ]] && continue + rm -rf "$dst/$name" + cp -r "$item" "$dst/" + done +} + +sync_common() { + local src="$1" dst="$2" + sync_folder "$src/rules/common" "$dst/rules/common" + sync_folder "$src/skills/common" "$dst/skills/common" + sync_folder "$src/bin" "$dst/bin" +} + +common_differs() { + local src="$1" dst="$2" + local path + + for path in "rules/common" "skills/common" "bin"; do + if [[ ! -d "$src/$path" || ! -d "$dst/$path" ]]; then + return 0 + fi + if ! diff -qr "$src/$path" "$dst/$path" >/dev/null; then + return 0 + fi + done + + return 1 +} + +create_project_agent_ops_dirs() { + local agent_ops_dir="$1" + mkdir -p "$agent_ops_dir/rules/project/domain" + mkdir -p "$agent_ops_dir/rules/private" + mkdir -p "$agent_ops_dir/skills/project" +} + +copy_common_scaffold() { + local src="$1" dst="$2" + mkdir -p "$dst/rules" "$dst/skills" + cp "$src/.version" "$dst/" + rm -rf "$dst/bin" "$dst/rules/common" "$dst/skills/common" + cp -r "$src/bin" "$dst/" + cp -r "$src/rules/common" "$dst/rules/" + cp -r "$src/skills/common" "$dst/skills/" + create_project_agent_ops_dirs "$dst" +} + +discover_agent_ops_targets() { + local parent + parent="$(dirname "$PROJECT_ROOT")" + + find "$parent" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r candidate; do + local resolved + resolved="$(cd "$candidate" && pwd)" + [[ "$resolved" == "$PROJECT_ROOT" ]] && continue + [[ -d "$resolved/agent-ops" ]] || continue + echo "$resolved" + done +} + +agent_ops_git_paths() { + local include_ai_config="${1:-1}" + + printf '%s\n' "agent-ops/.version" + printf '%s\n' "agent-ops/bin" + printf '%s\n' "agent-ops/rules/common" + printf '%s\n' "agent-ops/skills/common" + + local f + for f in "${AGENT_OPS_ENTRY_FILES[@]}"; do + if [[ -e "$f" ]] || git ls-files --error-unmatch "$f" >/dev/null 2>&1; then + printf '%s\n' "$f" + fi + done + if [[ "$include_ai_config" != "1" || -f ".agent-ops-source" ]]; then + return + fi + for f in "${AGENT_OPS_AI_SYNC_FILES[@]}"; do + if [[ -e "$f" ]] || git ls-files --error-unmatch "$f" >/dev/null 2>&1; then + printf '%s\n' "$f" + fi + done +} + +commit_and_push_agent_ops_scope() { + local repo="$1" message="$2" + local include_ai_config="${3:-1}" + + if ! git -C "$repo" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -e "${RED}Error: git 저장소가 아닙니다: $repo${RESET}" + return 1 + fi + + ( + cd "$repo" + mapfile -t paths < <(agent_ops_git_paths "$include_ai_config") + git add -- "${paths[@]}" + if git ls-files --error-unmatch "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" >/dev/null 2>&1 \ + && grep -qxF "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" ".gitignore" 2>/dev/null; then + git rm --cached --quiet --ignore-unmatch "$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN" + paths+=("$AGENT_ROADMAP_CURRENT_LOCAL_PATTERN") + fi + + if git diff --cached --quiet -- "${paths[@]}"; then + echo " agent-ops 변경 없음: commit 건너뜀" + else + git commit -m "$message" -- "${paths[@]}" + fi + + git push + ) +} + +sync_framework_to_target() { + local target="$1" + local dst_ao="$target/agent-ops" + local src_ver + src_ver="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + + echo "▶ $(basename "$PROJECT_ROOT") → $(basename "$target")" + + if ! git -C "$target" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo -e "${RED}Error: git 저장소가 아닙니다: $target${RESET}" + return 1 + fi + + if [[ ! -d "$dst_ao" ]]; then + echo " 최초 진행: agent-ops 공통 scaffold 복사" + copy_common_scaffold "$SRC_AO" "$dst_ao" + apply_agent_ops_entry_files "$SRC_AO/rules/common/rules.md" "$target" + echo -e "${YELLOW} init-agent-ops 스킬로 초기화를 진행하세요.${RESET}" + else + echo " 이후 진행: common/ 동기화" + sync_common "$SRC_AO" "$dst_ao" + cp "$SRC_AO/.version" "$dst_ao/.version" + apply_agent_ops_entry_files "$SRC_AO/rules/common/rules.md" "$target" + fi + ensure_agent_ops_ai_ignore_config "$target" + + commit_and_push_agent_ops_scope "$target" "sync: agent-ops from $(basename "$PROJECT_ROOT") v$src_ver" + echo -e "${GREEN}✓ 완료 → $(basename "$target")${RESET}" +} + +# ── 사용법 ──────────────────────────────────────────────────────────────────── +usage() { + echo "사용법: $0 [--pull] [target]" + echo "" + echo " (기본) push 동기화" + echo " --pull target(공통 원본 repo, 기본 agentic-framework) → 현재 프로젝트 로 pull" + echo "" + echo " target: 폴더명 (동일 레벨 탐색) | 상대경로 | 절대경로" + echo " .agent-ops-source 원본 repo에서 target 생략 시 상위 폴더의 agent-ops 적용 프로젝트 전체 동기화" +} + +# ───────────────────────────────────────────────────────────────────────────── +PULL_MODE="0" +if [[ "${1:-}" == "--pull" ]]; then + PULL_MODE="1" + shift +fi + +TARGET_INPUT="${1:-}" + +SRC_AO="$AGENT_OPS_DIR" +IS_FRAMEWORK="$([[ -f "$PROJECT_ROOT/.agent-ops-source" ]] && echo "1" || echo "0")" + +# ── pull 모드: .agent-ops-source 원본 repo → 현재 프로젝트 ─────────────────── +if [[ "$PULL_MODE" == "1" ]]; then + if [[ -z "$TARGET_INPUT" ]]; then + usage; exit 1 + fi + TARGET="$(resolve_target "$TARGET_INPUT")" + if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 + fi + DST_AO="$TARGET/agent-ops" + + if [[ "$IS_FRAMEWORK" == "1" ]]; then + echo -e "${RED}Error: .agent-ops-source가 있는 공통 원본 repo에서는 --pull을 사용할 수 없습니다.${RESET}" + exit 1 + fi + if [[ ! -f "$TARGET/.agent-ops-source" ]]; then + echo -e "${RED}Error: target이 공통 원본 repo가 아닙니다 (.agent-ops-source 없음)${RESET}" + exit 1 + fi + SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + DST_VER="$(cat "$DST_AO/.version" 2>/dev/null || echo "0.0.0")" + echo "▶ $(basename "$TARGET") → $(basename "$PROJECT_ROOT") (pull)" + echo " 현재 버전: $SRC_VER | 원본 버전: $DST_VER" + sync_common "$DST_AO" "$SRC_AO" + cp "$DST_AO/.version" "$SRC_AO/.version" + apply_agent_ops_entry_files "$DST_AO/rules/common/rules.md" "$PROJECT_ROOT" + ensure_agent_ops_ai_ignore_config "$PROJECT_ROOT" + commit_and_push_agent_ops_scope "$PROJECT_ROOT" "sync: pull from agentic-framework v$DST_VER" + echo -e "${GREEN}✓ 완료 (pull v$DST_VER)${RESET}" + exit 0 +fi + +# ── .agent-ops-source 원본 repo에서 다른 프로젝트로 ────────────────────────── +if [[ "$IS_FRAMEWORK" == "1" ]]; then + SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" + echo " .agent-ops-source 감지: push 동기화에서는 버전 bump 없이 v$SRC_VER 그대로 전파" + if [[ -n "$TARGET_INPUT" ]]; then + TARGET="$(resolve_target "$TARGET_INPUT")" + if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 + fi + sync_framework_to_target "$TARGET" + else + mapfile -t TARGETS < <(discover_agent_ops_targets) + if [[ "${#TARGETS[@]}" -eq 0 ]]; then + echo -e "${YELLOW}agent-ops가 적용된 sibling 프로젝트가 없습니다.${RESET}" + exit 0 + fi + + STATUS=0 + for TARGET in "${TARGETS[@]}"; do + if ! sync_framework_to_target "$TARGET"; then + STATUS=1 + fi + done + exit "$STATUS" + fi + exit 0 +fi + +# ── 일반 프로젝트에서 .agent-ops-source 원본 repo로 (push) ─────────────────── +if [[ -z "$TARGET_INPUT" ]]; then + TARGET_INPUT="agentic-framework" +fi + +TARGET="$(resolve_target "$TARGET_INPUT")" +if [[ -z "$TARGET" ]]; then + echo -e "${RED}Error: 대상을 찾을 수 없습니다: $TARGET_INPUT${RESET}" + exit 1 +fi +DST_AO="$TARGET/agent-ops" + +echo "▶ $(basename "$PROJECT_ROOT") → $(basename "$TARGET")" + +if [[ ! -f "$TARGET/.agent-ops-source" ]]; then + echo -e "${RED}Error: target이 공통 원본 repo가 아닙니다 (.agent-ops-source 없음)${RESET}" + exit 1 +fi + +SRC_VER="$(cat "$SRC_AO/.version" 2>/dev/null || echo "0.0.0")" +DST_VER="$(cat "$DST_AO/.version" 2>/dev/null || echo "0.0.0")" +echo " 현재 버전: $SRC_VER | 원본 버전: $DST_VER" + +COMMON_CHANGED="1" +if ! common_differs "$SRC_AO" "$DST_AO"; then + COMMON_CHANGED="0" +fi + +if [[ "$COMMON_CHANGED" == "1" ]] && version_gt "$DST_VER" "$SRC_VER"; then + echo -e "${RED}⚠ 버전 충돌: 원본 repo($DST_VER) > current($SRC_VER)${RESET}" + echo -e "${YELLOW} 먼저 sync-pull로 내려받은 뒤 재시도하세요.${RESET}" + exit 1 +fi + +if [[ "$COMMON_CHANGED" == "0" ]]; then + echo " 공통 파일 변경 없음: .version 차이는 무시하고 버전 갱신/커밋을 건너뜀" + echo -e "${GREEN}✓ 완료 (공통 변경 없음)${RESET}" + exit 0 +fi + +NEW_VER="$(bump_version "$SRC_VER")" +sync_common "$SRC_AO" "$DST_AO" +echo "$NEW_VER" > "$SRC_AO/.version" +echo "$NEW_VER" > "$DST_AO/.version" + +commit_and_push_agent_ops_scope "$TARGET" "sync: from $(basename "$PROJECT_ROOT") v$NEW_VER" "0" +commit_and_push_agent_ops_scope "$PROJECT_ROOT" "sync: to agentic-framework v$NEW_VER" "0" + +echo -e "${GREEN}✓ 완료 (v$SRC_VER → v$NEW_VER)${RESET}" diff --git a/agent-ops/rules/common/_templates/domain-rule-template.md b/agent-ops/rules/common/_templates/domain-rule-template.md new file mode 100644 index 0000000..a2ea36f --- /dev/null +++ b/agent-ops/rules/common/_templates/domain-rule-template.md @@ -0,0 +1,35 @@ +--- +domain: +last_rule_review_commit: +last_rule_updated_at: +--- + +# + +## 목적 / 책임 + +<이 도메인이 담당하는 책임을 1~2문장으로> + +## 포함 경로 + +- `/` — <이 경로가 이 도메인에 속하는 이유> + +## 제외 경로 + +- `/` — <왜 이 도메인이 아닌지> + +## 주요 구성 요소 + +- `` — <역할> + +## 유지할 패턴 + +- <네이밍 규칙 또는 아키텍처 패턴> + +## 다른 도메인과의 경계 + +- **<인접 domain>**: <어디까지가 이 도메인이고 어디서부터 저 도메인인지> + +## 금지 사항 + +- <이 도메인 코드에서 하면 안 되는 것> diff --git a/agent-ops/rules/common/_templates/test-case-rule-template.md b/agent-ops/rules/common/_templates/test-case-rule-template.md new file mode 100644 index 0000000..536adba --- /dev/null +++ b/agent-ops/rules/common/_templates/test-case-rule-template.md @@ -0,0 +1,79 @@ +--- +test_env: +test_profile: +domain: +verification_type: +last_rule_updated_at: +--- + +# 테스트 + +## 읽기 조건 + +- <이 도메인/검증 시나리오의 테스트, 검증, 실행 조건 판단이 필요한 경우> + +## 적용 범위 + +- <검증 대상 entrypoint, command, API, UX, profile, service> + +## 분류 + +- domain: +- verification_type: +- scope: <검증 대상 변경 범위 요약> + +## 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 명령 + +- setup: +- lint: +- unit: +- smoke: +- e2e: +- model: +- full-cycle: + +## 필수 검증 + +- <작업 완료 전 반드시 실행하거나 판단해야 하는 검증> + +## 보조 검증 + +- <실행하면 좋지만 필수 검증을 대체하지 않는 smoke/helper 검증> + +## 판정 기준 + +- <통과로 판단할 출력, 상태, UX, artifact, endpoint 조건> + +## 기준 출력 예시 + +```text +<필요한 경우 기대 출력 예시> +``` + +## 차단 기준 + +- <테스트를 계속할 수 없는 환경/권한/도구/서비스 조건> + +## 보고 항목 + +- 실행한 명령: +- 성공한 검증: +- 실패/차단된 검증: +- 생략 사유: +- 남은 위험: + +## 금지 사항 + +- <이 도메인/검증 시나리오에서 하면 안 되는 것> +- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다. diff --git a/agent-ops/rules/common/_templates/test-env-rules-template.md b/agent-ops/rules/common/_templates/test-env-rules-template.md new file mode 100644 index 0000000..66cca94 --- /dev/null +++ b/agent-ops/rules/common/_templates/test-env-rules-template.md @@ -0,0 +1,38 @@ +--- +test_env: +last_rule_updated_at: +--- + +# 테스트 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +## 공통 규칙 + +- 테스트/검증은 이 파일을 기준으로 판단한다. +- 작업 완료 검증은 변경 범위 기준으로 선택한다. +- 필수 검증을 실행하지 못하면 차단 사유로 보고한다. +- 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다. +- 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다. + +## 기본 환경 + +- host: +- port: +- runtime: +- package manager: +- docker: +- external service: +- model endpoint: +- credential: + +## 라우팅 + +- / / : `agent-test//.md` + +## 라우팅 규칙 + +- 여러 항목이 맞으면 모두 읽는다. +- 도메인 매핑이나 domain rule이 있으면 각 도메인의 `-smoke` 문서를 기본 baseline으로 둔다. +- 도메인이 아직 없을 때만 `project-smoke`를 fallback baseline으로 둔다. +- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다. diff --git a/agent-ops/rules/common/philosophy.md b/agent-ops/rules/common/philosophy.md new file mode 100644 index 0000000..b1b41f7 --- /dev/null +++ b/agent-ops/rules/common/philosophy.md @@ -0,0 +1,98 @@ +# Agent-Ops 철학 + +이 문서는 agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 읽는다. +일반 구현 작업에서는 읽지 않는다. + +## 핵심 + +- agent-ops는 AI agent가 작업하기 위한 규칙이자 가이드다. +- 사람 문서처럼 장황하게 설명하지 않고, agent가 바로 실행할 수 있게 작성한다. +- 필요한 컨텍스트만 읽게 만든다. 모든 문서를 항상 읽게 만들지 않는다. +- 애매한 형식을 만들지 않는다. 경로, 상태, id, 입력, 출력은 판별 가능해야 한다. +- 문서는 짧고 단단해야 한다. 길어서 이해되는 문서보다 짧아서 헷갈리지 않는 문서가 낫다. + +## 문서 작성 + +- 규칙과 스킬은 핵심만 쓴다. +- 같은 말을 여러 문서에 반복하지 않는다. 한 곳에 두고 링크한다. +- 설명보다 조건, 입력, 행동, 금지 사항을 우선한다. +- "적절히", "필요하면", "가능하면" 같은 말은 판별 기준이 없으면 쓰지 않는다. +- 예외가 있으면 예외 조건을 같이 쓴다. +- 긴 배경 설명은 README나 별도 참조 문서로 보내고, 실행 문서에는 실행 규칙만 남긴다. +- 룰 문서는 협업자가 직접 읽는 계약 문서이므로 한국어 `한다`체로 작성한다. +- README, GUIDE, roadmap 문서는 사람이 함께 검토하는 협업 문서이므로 한국어 설명체 또는 존댓말을 사용할 수 있다. +- 스킬 문서는 실행 안정성을 우선한다. 한국어 또는 영어를 사용할 수 있고, 이미 잘 동작하는 절차 계약은 언어 통일만을 위해 수정하지 않는다. +- path, filename, 상태값, id, regex, command, frontmatter key, runtime protocol token은 원문 ASCII 식별자를 유지한다. + +## 라우팅 + +- 라우팅은 얕아야 한다. +- 1홉은 진입 파일에서 공통/프로젝트 규칙을 읽는 단계다. +- 2홉은 규칙에서 domain rule, roadmap rule, router를 따라가는 단계다. +- 3홉은 router에서 SKILL.md를 읽는 단계다. +- 4홉은 skill이 템플릿이나 참조 문서를 추가로 읽는 단계다. +- 4홉 이상이 필요하면 구조가 과하게 쪼개졌는지 먼저 의심한다. 필요하면 앞 문서에 바로 가는 링크를 추가한다. +- 깊은 링크 체인을 만들지 않고, 필요한 문서가 무엇인지 앞 문서에서 바로 보이게 한다. +- 일반 작업마다 router, 모든 skill, 전체 roadmap, archive를 읽게 만들지 않는다. + +## LLM과 런타임 + +- LLM은 의미 판단, 범위 판단, 요약, 설계 선택을 맡는다. +- 런타임은 파일명, 폴더명, 상태값, exit code처럼 결정적으로 판별 가능한 일을 맡는다. +- LLM 없이 처리할 수 있는 구간은 파일 규약으로 뺀다. +- 런타임 신호는 문서 본문보다 경로와 이름에 둔다. +- 런타임 신호를 만들 때는 agent가 본문을 읽지 않아도 판별 가능해야 한다. +- `m-` 같은 prefix는 런타임 판별을 위한 신호다. +- code-review는 PASS 산출물을 만들고 완료 이벤트 메타데이터를 남긴다. +- roadmap 반영 여부와 호출 타이밍은 런타임이 완료 이벤트를 보고 판단한다. + +## 스킬 그룹 경계 + +- 스킬 그룹은 자기 라이프사이클 안에서만 강한 계약을 가진다. +- 스킬 그룹끼리는 다른 그룹의 내부 절차를 자동 호출하거나 전제하지 않는다. +- 그룹 간 연결은 사용자 명령, 런타임 이벤트, 또는 판별 가능한 파일 상태로만 한다. +- 구현 작업 그룹은 `plan`과 `code-review`의 PLAN/CODE_REVIEW 루프를 소유한다. +- 로드맵 그룹은 `create-roadmap`과 `update-roadmap`의 문서 작성, 의미 판단, 배치 제안, file-based fallback 갱신 흐름을 소유한다. +- Phase/Milestone 상태 전환, 구현 잠금 동기화, archive 이동, 완료 이벤트 반영처럼 결정적으로 판별 가능한 action은 NomadCode Core 또는 런타임이 있으면 그쪽 책임으로 둔다. +- `code-review`가 남긴 PASS 완료 이벤트를 로드맵에 반영할지, 언제 반영할지는 런타임이 판단하고 Core/MCP action이 있으면 그쪽으로, 없으면 `update-roadmap` file-based fallback 흐름으로 넘긴다. +- cross-project 잠금이나 `.agent-roadmap-sync` 같은 workspace-level 상태는 로드맵 그룹, Core, 런타임의 공동 경계다. `plan`과 `code-review`에 의존성 동기화 규칙을 넣지 않는다. +- 새 스킬을 만들기 전에 기존 그룹의 자연스러운 입력, 상태 전환, 후처리 지점에 넣을 수 있는지 먼저 본다. 다만 다른 그룹 경계를 침범해야 하면 런타임 이벤트로 분리한다. + +## 로드맵 + +- roadmap은 장기 기억이고, agent-task는 실행 상태다. +- current는 현재 작업 하나가 아니라 브랜치별 로컬 활성 Phase/Milestone 후보 창이다. +- Phase와 Milestone은 큰 방향과 기능 단위를 담는다. +- 구현 계획은 agent-task의 PLAN/CODE_REVIEW 루프에 둔다. +- 완료 후보는 바로 archive하지 말고 `[검토중]`으로 둔다. +- archive는 일반 작업에서 읽지 않는다. 과거 근거가 필요할 때만 링크를 따라 읽는다. + +## 스킬 + +- skill은 절차 문서다. +- skill 하나에 책임 하나만 둔다. +- skill이 다른 skill을 자동으로 깊게 호출하는 구조를 만들지 않는다. +- skill 본문은 실행에 필요한 규칙만 둔다. +- 템플릿은 출력 형식이 흔들릴 때만 둔다. +- 스킬 업데이트 시 router, rules, template, 출력 형식이 같은 계약을 말하는지 같이 확인한다. +- plan과 code-review처럼 짝 계약을 양쪽에서 반복해 강제하는 구조는 의도된 중복으로 본다. 동작 중인 짝 계약은 일관성 정리만을 위해 합치지 않는다. + +## 좋은 구조 + +- 진입 파일은 최소 규칙만 둔다. +- common rules는 공통 시작점만 둔다. +- project rules는 프로젝트 특화 판단만 둔다. +- domain rules는 특정 코드 영역 규칙만 둔다. +- skills는 반복 작업 절차만 둔다. +- roadmap은 장기 목표와 기능 단위만 둔다. +- agent-task는 실행 중인 작업 상태와 완료 산출물만 둔다. + +## 경고 신호 + +- 같은 내용을 세 군데 이상 설명하고 있다. +- 어떤 문서를 읽어야 할지 문서 안에서 다시 찾아야 한다. +- 상태값이 사람은 이해하지만 런타임은 판별하기 어렵다. +- skill이 너무 많은 예외를 품고 있다. +- README가 내부 규칙 문서처럼 길어지고 있다. +- archive를 일반 작업 컨텍스트로 끌어오고 있다. +- LLM이 파일명만 봐도 될 일을 본문까지 읽어 판단하고 있다. diff --git a/agent-ops/rules/common/rules-roadmap.md b/agent-ops/rules/common/rules-roadmap.md new file mode 100644 index 0000000..0e0da16 --- /dev/null +++ b/agent-ops/rules/common/rules-roadmap.md @@ -0,0 +1,158 @@ +# 로드맵 규칙 + +`agent-roadmap/` 디렉터리가 있는 프로젝트에서만 적용한다. + +## 구조 + +- 최상위 로드맵은 `agent-roadmap/ROADMAP.md`다. +- 활성 Phase는 `agent-roadmap/phase//PHASE.md`에 둔다. +- 활성 Milestone은 해당 Phase 아래 `agent-roadmap/phase//milestones/.md`에 둔다. +- ``와 ``는 소문자 영문, 숫자, 하이픈만 사용한다. +- 완료된 Phase는 scaffold 그대로 `agent-roadmap/archive/phase//PHASE.md`로 이동하고, 하위 Milestone도 `archive/phase//milestones/` 아래에 둔다. +- 진행중 Phase 안에서 완료된 Milestone은 활성 `PHASE.md`에 짧은 archive 링크를 남기고, 상세 문서는 `agent-roadmap/archive/phase//milestones/`로 이동한다. +- archive `PHASE.md`는 Phase 자체가 완료/폐기될 때만 만들며, 진행중 Phase의 완료 Milestone만 archive된 경우 archive Phase 디렉터리에 `milestones/`만 있을 수 있다. + +## Runtime Action Boundary + +- 로드맵 스킬은 기본적으로 로드맵 문서 작성, 의미 판단, 배치 제안, file-based fallback 갱신을 담당한다. +- Core/MCP가 있는 프로젝트에서는 Phase/Milestone 상태 전환, archive 이동, 외부 의존 lock 동기화, 완료 이벤트 반영 같은 action을 Core/MCP 또는 런타임이 처리한다. +- Core/MCP가 없거나 아직 해당 action을 제공하지 않는 프로젝트에서만 `update-roadmap` 스킬이 file-based fallback으로 직접 문서를 갱신한다. +- `update-roadmap`은 fallback 갱신을 수행하더라도 런타임 action 경계를 문서화하고, Core/MCP로 넘길 수 있는 입력과 결과를 함께 남긴다. + +## 로딩 + +- 세션 최초 1회 `agent-roadmap/current.md`가 있으면 읽고 활성 Phase, 활성 Milestone의 이름, 경로, 선택 규칙만 짧게 기억한다. +- `agent-roadmap/current.md`는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. +- `current.md`가 없고 로드맵 기반 계획 또는 갱신이 필요하면 `agent-ops/skills/common/_templates/roadmap-current-template.md` 형식으로 로컬 파일을 만들거나 `ROADMAP.md`와 활성 `PHASE.md`에서 후보를 고른다. +- 읽기 전용 로드맵 현지점 확인에서는 `current.md`가 없어도 만들지 않고, 로컬 current 없음으로 보고한 뒤 `ROADMAP.md`의 Phase 흐름만 보여준다. +- 일반 작업에서는 `ROADMAP.md`를 읽지 않는다. +- 일반 작업에서는 `agent-roadmap/archive/**`를 읽지 않는다. +- 기능 추가, 구조 변경, 구현 계획 전에는 요청과 변경 파일에 맞는 활성 Phase와 활성 Milestone 문서를 읽는다. +- 로드맵 현지점 확인은 로컬 `current.md`, `ROADMAP.md`의 Phase 흐름, 활성 `PHASE.md`의 Milestone 흐름, 활성 Milestone의 제목/목표/상태만 기본으로 읽는다. +- `ROADMAP.md`는 로드맵 생성/갱신, Phase 추가/삭제/전환, 전체 구조 변경, 활성 범위 밖 작업 확인 때만 읽는다. +- 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `ROADMAP.md` 또는 `PHASE.md`에 있는 archive 링크를 따라가서 필요한 archive 문서만 읽는다. + +## Phase와 Milestone 선택 + +- `current.md`는 현재 작업 위치가 아니라 활성 Phase와 활성 Milestone 후보 목록이다. +- `current.md`는 공유 진행 상태가 아니며, 공유해야 할 상태는 `ROADMAP.md`, `PHASE.md`, Milestone 문서, `.agent-roadmap-sync/locks.yaml`에 남긴다. +- 활성 Phase는 `agent-roadmap/phase/**/PHASE.md`만 대상으로 한다. +- 활성 Milestone은 `agent-roadmap/phase/**/milestones/*.md`만 대상으로 한다. +- `current.md`에는 `[완료]` 또는 `[폐기]` Phase/Milestone을 남기지 않는다. 완료 후보는 승인 전까지 `[검토중]`으로 둔다. +- "로드맵에 추가", "마일스톤에 추가"처럼 target 없는 신규 작업 추가 요청은 `update-roadmap` 스킬로 배치 제안 또는 file-based fallback 갱신을 처리하고, Phase/Milestone/Epic/Task 배치를 자동 판단한다. +- target 없는 신규 추가 요청은 먼저 요청 규모를 `phase`, `milestone`, `epic`, `task`, `subtask`, `context` 중 가장 작은 충분한 단위로 판정한다. +- target 없는 신규 추가 요청은 활성 창만으로 결정하지 말고 필요한 경우 `ROADMAP.md`의 Phase 흐름과 관련 Phase/Milestone 문서를 비교한다. +- 배치는 Phase -> Milestone -> Epic -> Task 순서로 내려가며 같은 레벨의 동일/유사 후보를 먼저 찾는다. +- 동일/유사 항목이 이미 있으면 새로 만들지 말고 기존 항목을 업데이트한다. +- 적절한 기존 후보가 없을 때만 판정한 규모에 맞는 새 항목을 만든다. +- 부모 후보는 있고 판정 규모의 항목만 없으면 부모 아래에 새 항목을 만들고, 부모도 없을 때만 필요한 부모 항목을 함께 만든다. +- 자동 배치할 때는 선택한 Phase/Milestone/Epic/Task와 밀린 후보의 이유를 결과에 남긴다. +- `current.md`가 아카이브 경로를 가리키면 해당 항목은 활성 후보로 읽지 말고 로드맵 갱신이 필요하다고 보고한다. +- 선택한 Phase/Milestone의 목표 또는 범위 제외와 요청이 충돌하면 구현 전에 사용자에게 확인한다. + +## 상태 표기 + +- Phase와 Milestone 상태 표기는 `[스케치]`, `[계획]`, `[진행중]`, `[검토중]`, `[완료]`, `[보류]`, `[폐기]` 중 하나만 사용한다. +- `[스케치]`는 방향성, 문제의식, 후보 범위, 미정 질문을 기록하는 컨셉 상태다. 구현 가능한 계획이 아니므로 `agent-task` 구현 계획 생성과 코드 구현을 시작하지 않는다. +- `[스케치]` 항목은 `[계획]`으로 승격하기 위한 `승격 조건`, 사용자 결정, 범위 경계, 후속 Milestone 후보를 정리하는 것이 목적이다. +- `[계획]` 이상 상태의 Milestone에서 `승격 조건` 섹션은 선택 사항이다. 섹션이 없거나 `- 없음`이면 템플릿 오류로 보지 않는다. +- `[스케치]`를 `[계획]`으로 전환하려면 `승격 조건`의 미정 항목이 해소되고, 목표, 범위, 기능 Task, 직접적인 사용자 결정 항목, 후속 구현 단위가 구현 계획을 만들 수 있을 만큼 정리되어야 한다. +- `[계획]`은 목표, 범위, 기능 Task, 구현 잠금, 결정 필요 항목이 정리되어 구현 계획을 만들 수 있는 상태다. +- 갱신 범위에 포함된 기존 진행 상태 표기는 `[진행중]`으로 정리한다. +- `[검토중]`은 모든 기능 Task와 Task 안에 명시된 검증이 충족된 것으로 보이나, 사용자의 최종 완료 확인과 archive 승인이 아직 남은 완료 후보 상태다. +- `[검토중]` 항목은 활성 경로에 남기고 `current.md`의 활성 후보로 유지할 수 있다. +- 검토 결과 보완이 필요하면 별도 reopen 상태를 만들지 않고 `[진행중]`으로 되돌린 뒤 `완료 리뷰` 또는 `작업 컨텍스트`에 보완 방향을 남긴다. +- 검토 결과 보류 또는 폐기 결정이 나면 `[보류]` 또는 `[폐기]`로 전환한다. +- `ROADMAP.md`의 Phase 흐름과 `PHASE.md`의 Milestone 흐름은 완료, 검토중, 진행중, 계획, 스케치 순서를 기본으로 하며 아래로 갈수록 미래 작업에 가까워지게 정렬한다. + +## 구현 잠금 + +- `구현 잠금`은 승인 의식이 아니라 사용자 결정이 필요한지 표시하는 얇은 상태다. +- 제품 방향, 범위, 우선순위, 책임 경계처럼 사용자만 결정할 수 있는 항목이 남아 있으면 상태를 `잠금`으로 두고 `결정 필요` 체크리스트에 질문을 적는다. +- 기존 구조, 도메인 rule, 플랫폼 관례, 업계 표준으로 합리적으로 정할 수 있는 항목은 `결정 필요`가 아니라 `작업 컨텍스트`의 표준선이나 구현 가정으로 기록한다. +- Milestone 전체에서 사용자만 결정할 항목이 더 이상 없고 에이전트가 표준선에 따라 실행하면 되는 상태라면 `해제`로 둔다. +- 선택한 Milestone에 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 코드 구현, `agent-task` 구현 계획, 세부 API/파일 구조 확정을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 사용자에게 확인한다. +- 현재 요청과 직접 관련 없는 미정 항목은 잠금 상태로 남겨도 되며, 표준선으로 처리 가능한 작업을 막지 않는다. +- 잠금 상태를 바꾸더라도 `기능` Task를 자동 완료 처리하지 않는다. +- `[스케치]` 상태의 Milestone은 `구현 잠금`이 `해제`로 보이더라도 구현 계획과 코드 구현 대상이 아니다. 먼저 `[계획]`으로 승격해야 한다. + +## 프로젝트 간 잠금 + +- 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`은 프로젝트 간 Milestone 잠금 인덱스다. 외부 의존 잠금을 생성하거나 동기화해야 하면 Core/MCP가 있으면 그쪽 action이 만들고, 없으면 `update-roadmap` file-based fallback이 디렉터리와 파일을 만든다. +- entry는 `id`, `locked`, `rely-on[].target`, `rely-on[].status`, `rely-on[].note`만 사용한다. +- `locks.yaml`은 root sequence block style을 기본으로 작성한다. 예: `- id: ...` 아래에 `locked`, `rely-on`을 둔다. +- `id`는 기본적으로 `<잠긴-project>:<잠긴-milestone-slug>`로 만든다. +- `locked`와 `rely-on[].target`은 `:agent-roadmap/phase//milestones/.md` 형식으로 기록한다. +- Milestone 경로가 `agent-roadmap/...` 상대 경로이면 현재 프로젝트명을 prefix로 붙인다. workspace 하위 절대/상대 경로이면 workspace 바로 아래 디렉터리명을 project로 삼고, 그 뒤 `agent-roadmap/...` 경로를 붙인다. +- `locked`는 잠긴 Milestone, `rely-on.target`은 선행 조건 Milestone이다. 둘 다 같은 workspace의 어느 활성 Phase 하위 Milestone이어도 된다. 의존 대상이 `current.md`에 있어야 한다고 가정하지 않는다. +- "현재 마일스톤은 X 프로젝트 작업 뒤에 진행", "X 프로젝트 때문에 현재 작업 잠금", "의존성 설정해"처럼 잠긴 Milestone을 생략한 외부 의존 잠금 요청은 현재 프로젝트 로컬 `current.md`의 활성 Milestone 단일 후보를 잠긴 대상으로 삼는다. +- 의존 대상은 명시 경로, 명시 slug, 명시 제목, 잠긴 Milestone 문서의 선행 Milestone 힌트, 대상 프로젝트 로컬 `current.md`가 있을 때의 단일 후보 순서로 확정한다. +- 정규화 비교는 소문자 변환, backtick/따옴표 제거, 영문/숫자가 아닌 연속 문자를 `-` 하나로 치환, 앞뒤 `-` 제거 후 Milestone 파일 slug와 정규화한 제목에 대조한다. +- 의존 대상 탐색은 대상 프로젝트의 `agent-roadmap/phase/*/milestones/*.md` 활성 문서만 대상으로 한다. archive 문서는 사용자가 archive 경로를 명시한 경우 외에는 읽거나 후보로 삼지 않는다. +- 후보가 없거나 둘 이상이면 locks.yaml을 만들거나 고치지 말고 사용자에게 대상 Milestone 선택을 요청한다. +- 외부 의존 잠금을 만들 때 대상 Milestone의 `구현 잠금`은 `잠금`으로 둔다. +- 새 `rely-on.status`는 선행 Milestone 상태에서 파생한다. `[검토중]` 또는 `[완료]`이면 `enable`, 그 외 상태거나 상태를 확인할 수 없으면 `disable`이다. +- 같은 `id` entry를 upsert할 때 기존 `rely-on` 항목을 삭제하지 않는다. 같은 `rely-on.target`만 status/note를 갱신하고, 없는 target은 추가하며, `locked` 경로가 바뀐 경우에만 `locked`를 갱신한다. +- `locks.yaml`이 있고 Milestone을 갱신하거나 archive할 때는 대상 Milestone identity로 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" both ""`를 먼저 실행한다. +- find 결과가 `none`이면 결과 보고의 `Workspace 잠금`에 `관련 lock 없음`을 남긴다. 외부 의존 잠금 생성/동기화 요청이 아니라면 `locks.yaml`을 새로 만들거나 수정하지 않는다. +- Core/MCP action 또는 `update-roadmap` fallback이 갱신한 Milestone identity가 어느 entry의 `rely-on.target`과 일치하면 Milestone 상태 기준으로 `status`를 동기화한다. `[검토중]` 또는 `[완료]`이면 `enable`, 그 외 상태면 `disable`이다. +- Core/MCP action 또는 `update-roadmap` fallback이 갱신하거나 선택한 Milestone identity가 어느 entry의 `locked`와 일치하면 모든 `rely-on.status`가 `enable`인지 결과 보고에 남긴다. 모든 조건이 충족되어도 잠금 해제 실행은 Core/MCP 또는 런타임의 별도 action으로 처리한다. +- archive 모드에서는 파일 이동 전에 대상 Milestone의 활성 경로 identity를 보존하고, 그 identity로 `--find-milestone "" both ""`를 먼저 실행한다. 보존한 identity가 어느 entry의 `rely-on.target`과 일치하고 Milestone 상태가 `[완료]`이면 archive 이동 전에 해당 `rely-on.status`를 `enable`로 바꾼다. +- archive 모드에서 보존한 identity가 어느 entry의 `locked`와 일치하면 archive 이동 전에 모든 `rely-on.status`가 `enable`인지 결과 보고에 남긴다. 미충족이어도 archive 자체는 막지 않고 `Workspace 잠금: 미충족`으로 보고한다. +- 잠금 해제 조건 충족 여부만 확인할 때는 `agent-ops/skills/common/check-roadmap-dependency/SKILL.md`를 읽는다. lock id가 없으면 해당 스킬은 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" "" ""`로 현재 Milestone이 `locked`인지 `rely-on.target`인지 양방향으로 찾은 뒤 `agent-ops/bin/roadmap-dependency-checker.sh "" ""`를 사용한다. +- checker exit code는 `0=true`, `1=false`, `2=설정/입력/파싱 오류`로 해석한다. + +## Epic과 Task id + +- Milestone 문서의 실행 체크리스트는 `기능` 섹션 하나로 작성한다. 새 Milestone이나 갱신 범위에 포함된 Milestone에는 별도 `완료 기준` 섹션을 만들지 않는다. +- 기존 Milestone에 `필수 기능`과 `완료 기준`이 분리되어 있으면, 갱신 시 `완료 기준`을 관련 기능 Task 안의 선택적 `검증:` 문구로 흡수하고 섹션을 제거한다. +- 기능 Task는 기능 또는 산출물 단위다. 검증이 필요한 기능만 같은 Task 안에 `검증: <명령/확인 방법/기대 결과>`를 붙인다. +- 검증이 명시된 Task의 `[x]`는 기능/산출물과 해당 검증이 모두 충족되었다는 뜻이다. 검증이 명시되지 않은 Task의 `[x]`는 기능/산출물 완료 근거가 충분하다는 뜻이다. +- 사용자만 결정할 수 있는 검토/선택/우선순위 항목은 기능 Task로 쓰지 않는다. `구현 잠금`의 `결정 필요` 또는 `작업 컨텍스트`로 분리하고, 현재 구현에 직접 필요하면 plan 생성 전에 사용자 확인을 받는다. +- `기능` 섹션의 Task 체크리스트는 Epic 바로 아래의 flat list를 기본으로 한다. 구현 세부, 테스트만 따로 떼어낸 하위 체크박스는 roadmap에 만들지 말고 plan 내부 체크리스트나 같은 Task의 `검증:`으로 흡수한다. +- Epic heading은 `### Epic: [epic-id] <이름>` 형식을 사용한다. +- Task는 `- [ ] [item-id] 설명` 또는 `- [x] [item-id] 설명` 형식을 사용한다. +- epic-id와 item-id는 공백 없는 짧은 ASCII 토큰이며, 영문/숫자 segment 1~4개로 작성하고 segment 구분자는 `-`, `_`, `+`, `=`만 사용한다. 가능하면 1~3 segment를 우선하며, 전체 길이는 32자 이하를 권장한다. +- epic-id와 item-id는 해당 Milestone 안에서만 유일하면 된다. +- 다른 Milestone에서는 같은 id를 다시 사용할 수 있다. 여러 Milestone 후보에서 같은 id가 발견되면 Milestone 이름이나 문서 경로로 대상을 확정한다. +- 사용자가 epic-id 또는 item-id를 언급하면 해당 Milestone의 Epic/Task 항목을 우선 anchor로 삼고, 기존 id는 명시적 요청 없이 바꾸지 않는다. + +## Milestone 기반 agent-task + +- `plan` 스킬이 활성 Milestone 범위의 구현 계획을 만들면 task group은 `agent-task/m-/` 형식을 사용한다. +- ``는 활성 Milestone 파일명에서 `.md`를 제거한 값이며, Phase slug, Epic id, Task id, 별도 task slug를 task group에 넣지 않는다. +- split 작업은 기존 규칙 그대로 `agent-task/m-//` 아래에 둔다. +- `m-`는 Milestone 기반 작업 전용 예약 prefix이며, 일반 작업 task group은 `m-`으로 시작하지 않는다. +- 런타임은 파일 내부가 아니라 task group 이름만으로 Milestone 기반 작업 여부를 판별한다. +- `code-review`에서 `m-` 작업이 PASS되면 roadmap을 직접 수정하거나 `update-roadmap`을 직접 호출하지 않는다. +- 런타임은 PASS 완료 이벤트의 task group에서 `m-`를 판별하고, 상태 체크 후 Core/MCP action으로 Milestone 업데이트를 호출한다. Core/MCP action이 없으면 `update-roadmap` file-based fallback 흐름을 호출한다. 단, Milestone 기능 Task 체크는 `complete.log`에 `Roadmap Completion` 섹션과 명시 Task id가 있을 때만 수행하고, 섹션이 없으면 no-op으로 둔다. +- 런타임 완료 이벤트가 최종 archive 경로만 갖고 있으면 `agent-task/archive/YYYY/MM/m-/...`를 `agent-task/m-/...` 형태의 `origin-task`로 정규화해 전달한다. +- 런타임 호출에서 매칭되는 활성 Milestone이 없거나 둘 이상이면 추정하지 말고 수동 target 선택이 필요하다고 보고한다. +- `WARN` 또는 `FAIL`은 Milestone 완료 업데이트를 하지 않는다. 일반적으로 같은 `m-` task group에서 follow-up plan/review를 이어가지만, code-review의 user-review gate가 트리거되면 `USER_REVIEW.md`를 남기고 사용자 판단을 기다린다. +- `[스케치]` Milestone은 Milestone 기반 `agent-task` 생성 대상이 아니다. 런타임이나 plan 스킬은 이를 구현 작업으로 라우팅하지 않고 `[계획]` 승격 필요를 보고한다. + +## 완료 리뷰 + +- Task 완료나 Milestone 갱신 시 모든 기능 Task와 Task 안에 명시된 검증이 evidence와 함께 `[x]`가 되었는지 확인한다. +- 모두 충족된 것으로 보이면 Milestone을 `[완료]`로 바로 바꾸거나 archive로 이동하지 말고 `[검토중]`으로 바꾼다. +- `[검토중]`으로 바꿀 때는 Milestone 문서에 `완료 리뷰` 섹션을 만들거나 갱신하고, 완료 근거 1~3줄과 사용자에게 필요한 최종 확인 항목을 남긴다. +- 사용자가 완료를 승인한 뒤에만 `[완료]`로 전환하고 archive 이동을 수행한다. +- Phase도 모든 하위 Milestone이 `[완료]` 또는 `[폐기]`로 정리되어 Phase 완료 후보가 되면 `[검토중]`으로 두고 사용자 최종 확인을 받은 뒤 `[완료]` 또는 `[폐기]`로 전환한다. + +## 로드맵 현지점 + +- 현재 작업 지점이나 로드맵상 현 위치 확인 요청은 `analyze-roadmap-position` 스킬로 처리한다. +- 답변은 `agent-ops/skills/common/_templates/roadmap-position-report-template.md` 섹션과 필드 순서를 따른다. +- 기본 동작에서는 코드, git 상태, diff를 읽지 않고 `로드맵 > Phase > Milestone` breadcrumb와 흐름 목록으로 현재 좌표를 보여준다. +- current가 Phase 또는 Milestone 후보를 여럿 가리키면 모두 `현재 후보`로 표시하고 짧은 역할 태그만 붙인다. + +## 아카이브 + +- 완료 또는 폐기되어 현재 작업 후보에서 제외할 Phase/Milestone은 Core/MCP action으로 아카이빙한다. Core/MCP action이 없으면 `update-roadmap` file-based fallback으로 아카이빙한다. +- `[검토중]` Phase/Milestone은 archive 대상이 아니며, 사용자 승인 전까지 활성 경로에 남긴다. +- Phase 아카이브 대상은 `agent-roadmap/archive/phase//PHASE.md`와 같은 scaffold로 이동한다. +- Milestone 아카이브 대상은 `agent-roadmap/archive/phase//milestones/.md`로 이동한다. +- Milestone 아카이브 전에는 이동 전 활성 경로 identity로 `.agent-roadmap-sync/locks.yaml`을 확인한다. 해당 identity가 `rely-on.target`이면 `[완료]` 상태에서 `enable`로 동기화하고, 해당 identity가 `locked`이면 의존 조건 충족 여부를 보고하며, 어느 쪽에도 없으면 `관련 lock 없음`으로 보고한다. +- 아카이빙할 때는 활성 `ROADMAP.md` 또는 활성 `PHASE.md`에 archive 문서 링크와 짧은 요약만 남긴다. +- 아카이브된 Phase/Milestone은 로컬 `current.md`에 남기지 않고, 일반 Phase/Milestone 선택이나 위치 분석의 후보로 삼지 않는다. +- 아카이브 문서는 과거 기록 스냅샷으로 보고, 최신 템플릿이나 스킬 규약에 맞춰 재포맷하지 않는다. diff --git a/agent-ops/rules/common/rules.md b/agent-ops/rules/common/rules.md new file mode 100644 index 0000000..d0488a8 --- /dev/null +++ b/agent-ops/rules/common/rules.md @@ -0,0 +1,44 @@ +# 공통 규칙 + +**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.** + +- 기존 구조를 우선한다. 새 파일 생성보다 기존 파일 수정을 우선한다. +- 사용자에게 보이는 최종 답변과 설명은 기본적으로 한국어로 작성한다. +- 코드 변경 전 관련 domain rule을 먼저 확인한다. +- 요청 범위를 넘는 변경을 하지 않는다. +- 불확실하면 단정하지 말고 후보를 제시한다. +- `agent-task/archive/**`는 사용자가 명시적으로 요청한 경우에만 읽는다. 예외: plan/code-review 루프의 split subtask 선행 의존성 충족 여부를 확인할 때는 같은 task group의 후보 `complete.log`만 읽을 수 있다. +- `agent-roadmap/` 디렉터리가 있는 프로젝트에서도 `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 로드맵 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `agent-ops/rules/common/rules-roadmap.md`의 archive 접근 규칙을 따른다. +- agent-ops 구조, 규칙, 스킬, 로드맵, 런타임 책임 경계를 설계하거나 수정할 때만 `agent-ops/rules/common/philosophy.md`를 읽는다. +- tracked `docs/`는 사람용 최신 가이드와 공개 설명만 둔다. + +**세션 최초 1회 아래 파일을 순서대로 반드시 읽는다.** 파일이나 디렉터리가 없는 항목은 건너뛴다. 그외에 스킵은 금지한다. + +1. `agent-ops/rules/project/rules.md` +2. `agent-ops/rules/private/rules.md` +3. `agent-roadmap/` 디렉터리가 있으면 `agent-ops/rules/common/rules-roadmap.md` + +# 프로젝트 간 잠금 + +- "이 Milestone은 X가 끝나야 가능하다", "A 전까지 B를 잠근다", "잠금 해제 조건은 X다", "현재 마일스톤은 X 프로젝트 작업 뒤에 진행되어야 한다", "의존성 설정해"는 `update-roadmap`으로 처리한다. + +# 스킬 규칙 + +**아래 경우에 부합되는지 반드시 끝까지 정독해서 읽고, 부합할 경우 `agent-ops/skills/common/router.md`를 작업 최초 1회 읽고 수행한다.** 자동으로 수행하지 않는다. **절대 스킵하지 말고 정독해야한다** +- agent-ops 초기화 +- domain rule 생성 +- skill 생성 +- 테스트 룰 작성/생성/수정, 도메인별/검증 시나리오별 테스트 문서, create-test/update-test +- README 생성 +- 로드맵/마일스톤 생성·갱신 +- 로드맵 현지점 / 현재 작업 지점 확인 +- 계획 작성 / plan 생성 +- 코드 리뷰 / review 진행 +- git commit / push +- agent-ops 업데이트 / 진입 파일 재적용 + +# 테스트 규칙 + +**테스트 실행/검증 작업이 포함된 경우, 작업 환경에 맞게 최초 1회 읽고 수행한다. 환경 미지정은 local로 본다.** + +- local: `agent-test/local/rules.md` (없으면 `create-test`) diff --git a/agent-ops/skills/common/_templates/implementation-user-review-request-section.md b/agent-ops/skills/common/_templates/implementation-user-review-request-section.md new file mode 100644 index 0000000..bce0022 --- /dev/null +++ b/agent-ops/skills/common/_templates/implementation-user-review-request-section.md @@ -0,0 +1,11 @@ +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 diff --git a/agent-ops/skills/common/_templates/roadmap-current-template.md b/agent-ops/skills/common/_templates/roadmap-current-template.md new file mode 100644 index 0000000..3e4b94b --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-current-template.md @@ -0,0 +1,29 @@ +# 현재 로드맵 컨텍스트 + +## 활성 Phase + +- [<스케치 | 계획 | 진행중 | 검토중 | 보류>] + - 경로: `agent-roadmap/phase//PHASE.md` + +## 활성 Milestone + +- [<스케치 | 계획 | 진행중 | 검토중 | 보류>] + - Phase: `agent-roadmap/phase//PHASE.md` + - 경로: `agent-roadmap/phase//milestones/.md` + +## 선택 규칙 + +- 이 문서는 활성 Phase와 활성 Milestone 후보 목록이며, 개인별 현재 작업 위치나 완료 상태를 기록하지 않는다. +- 이 문서는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. +- 활성 Phase는 `agent-roadmap/phase//PHASE.md`를 가리킨다. +- 활성 Milestone은 `agent-roadmap/phase//milestones/.md`를 가리킨다. +- 활성 항목은 아카이브 경로를 포함하지 않는다. +- `[검토중]` 항목은 사용자 완료 확인 전까지 활성 항목으로 남길 수 있다. +- `[스케치]` 항목은 활성 후보로 남길 수 있지만 구현 계획 생성 대상은 아니며, `[계획]` 승격 조건을 정리하는 대상으로만 다룬다. +- `[완료]` 또는 `[폐기]` 항목은 archive 링크를 남긴 뒤 활성 항목에서 제거한다. +- 요청 내용, 현재 브랜치, 변경 파일, 관련 코드 경로를 보고 가장 관련 있는 Phase와 Milestone을 선택하고 같은 세션에서 1회 읽는다. +- 활성 Phase 또는 Milestone 둘 이상에 걸치면 필요한 문서를 모두 읽고 작업 범위를 좁힌다. +- 활성 범위 밖의 작업이면 `agent-roadmap/ROADMAP.md`의 Phase 흐름을 확인하고 사용자에게 진행 또는 전환 여부를 확인한다. +- 완료된 과거 내용이 필요할 때만 `ROADMAP.md` 또는 `PHASE.md`에 있는 archive 링크를 따라가서 읽는다. +- 선택된 Milestone의 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 구현이나 구현 계획을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 확인한다. 관련 결정이 없고 표준선으로 처리 가능하면 잠금을 유지한 채 진행할 수 있으며, Milestone 전체에서 사용자만 결정할 항목이 더 이상 없을 때만 `구현 잠금` 상태를 `해제`로 둔다. +- 선택된 Milestone 상태가 `[스케치]`이면 `구현 잠금` 상태와 관계없이 구현이나 구현 계획을 시작하지 않고 `[계획]` 승격 필요를 보고한다. diff --git a/agent-ops/skills/common/_templates/roadmap-milestone-template.md b/agent-ops/skills/common/_templates/roadmap-milestone-template.md new file mode 100644 index 0000000..669bc84 --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-milestone-template.md @@ -0,0 +1,85 @@ +# Milestone: + +## 위치 + +- Roadmap: `agent-roadmap/ROADMAP.md` +- Phase: `agent-roadmap/phase//PHASE.md` + +## 목표 + +<이 Milestone이 끝났을 때 달성되어야 하는 결과를 1~3문장으로 작성> + +## 상태 + +[<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + +## 승격 조건 + + + +- 없음 + + + +## 구현 잠금 + +- 상태: <잠금 | 해제> +- 결정 필요: <없음 | 아래 체크리스트> + - [ ] <사용자만 결정할 수 있는 제품/범위/우선순위/책임 경계 질문> + +## 범위 + +- <이 Milestone에 포함되는 제품/기술/문서 범위> + +## 기능 + + + +### Epic: [epic-id] + +<이 Epic이 묶는 capability 또는 산출물 설명> + +- [ ] [item-id] <구현 세부가 아니라 이 Milestone에서 달성해야 할 capability 또는 산출물> + + + +## 완료 리뷰 + +- 상태: <없음 | 요청됨 | 승인됨 | 보완 필요 | 보류 | 폐기> +- 요청일: +- 완료 근거: <모든 기능 Task와 Task 안에 명시된 검증 충족 여부를 1~3줄로 요약> +- 리뷰 필요: + - [ ] 사용자가 완료 결과를 확인했다 + - [ ] archive 이동을 승인했다 +- 리뷰 코멘트: <없음 | 보완/보류/폐기 방향성> + +## 범위 제외 + +- <이 Milestone에서 의도적으로 하지 않는 일> + +## 작업 컨텍스트 + +- 관련 경로: `` +- 표준선(선택): <기존 구조, 도메인 rule, 플랫폼 관례, 업계 표준으로 진행할 기본 기준> +- 선행 작업: <없음 또는 Milestone/태스크 이름> +- 후속 작업: <없음 또는 Milestone/태스크 이름> +- 확인 필요: diff --git a/agent-ops/skills/common/_templates/roadmap-phase-template.md b/agent-ops/skills/common/_templates/roadmap-phase-template.md new file mode 100644 index 0000000..08daa5d --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-phase-template.md @@ -0,0 +1,23 @@ +# Phase: + +## 상태 + +[<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + +## 목표 + +<이 Phase가 끝났을 때 달성되어야 하는 결과와 책임 경계를 1~3문장으로 작성> + +## Milestone 흐름 + +완료된 Milestone은 archive 경로를 가리키고, 검토중, 진행중, 계획, 스케치 또는 보류 Milestone은 이 Phase 하위 `milestones/` 경로를 가리킨다. +완료, 검토중, 진행중, 계획, 스케치 순서로 두어 아래로 갈수록 미래 작업에 가까워지게 정렬한다. +스케치 Milestone은 아직 구현 가능한 계획이 아니므로 계획 Milestone보다 아래에 둔다. + +- [<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + - 경로: `agent-roadmap/phase//milestones/.md` 또는 `agent-roadmap/archive/phase//milestones/.md` + - 요약: <목표 또는 결과 1문장> + +## Phase 경계 + +- <이 Phase에서 유지할 책임 경계 또는 범위 제외 기준> diff --git a/agent-ops/skills/common/_templates/roadmap-position-report-template.md b/agent-ops/skills/common/_templates/roadmap-position-report-template.md new file mode 100644 index 0000000..ea057ea --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-position-report-template.md @@ -0,0 +1,21 @@ +# 로드맵 현지점 + +- [local current.md](agent-roadmap/current.md) + +- [로드맵: ](agent-roadmap/ROADMAP.md) + - [Phase: ](agent-roadmap/phase//PHASE.md) + - [Milestone: ](agent-roadmap/phase//milestones/.md) + - [<상태>] <목표 또는 역할 한 줄> + - [Milestone: ](agent-roadmap/phase//milestones/.md) + - [<상태>] <목표 또는 역할 한 줄> + +## 전체 Phase 흐름 + +- [<상태>] []() +- [<상태>] [<현재 Phase 이름>]() ← 현재 +- [<상태>] []() + +## 현재 Phase의 Milestone 흐름 + +- [<상태>] []() ← 현재 후보, <역할 태그> +- [<상태>] []() ← 현재 후보, <역할 태그> diff --git a/agent-ops/skills/common/_templates/roadmap-template.md b/agent-ops/skills/common/_templates/roadmap-template.md new file mode 100644 index 0000000..b42dd53 --- /dev/null +++ b/agent-ops/skills/common/_templates/roadmap-template.md @@ -0,0 +1,42 @@ +# 로드맵 + +## 전체 목표 + +<프로젝트가 최종적으로 달성하려는 결과를 1~3문장으로 작성> + +## Phase 흐름 + +위에서 아래로 진행된 순서와 예정 흐름을 나타낸다. +완료된 Phase도 로드맵에서 제거하지 않고, archive의 Phase 문서로 연결한다. +검토중 또는 진행중 Phase는 계획 Phase보다 위에 두어, 아래로 갈수록 미래 작업에 가까워지게 정렬한다. +스케치 Phase는 아직 구현 가능한 계획이 아니므로 계획 Phase보다 아래에 둔다. + +- [<스케치 | 계획 | 진행중 | 검토중 | 완료 | 보류 | 폐기>] + - 경로: `agent-roadmap/phase//PHASE.md` 또는 `agent-roadmap/archive/phase//PHASE.md` + - 요약: <이 Phase의 목표와 역할 1문장> + +## 로딩 정책 + +- 일반 작업에서는 `agent-roadmap/ROADMAP.md`를 매번 읽지 않는다. +- 기능 추가, 구조 변경, 스킬 추가/수정, 문서 구조 변경 작업을 수행할 때는 `agent-roadmap/current.md`를 먼저 읽는다. +- `current.md`는 현재 작업 위치가 아니라 활성 Phase와 활성 Milestone 후보 목록이다. +- `current.md`는 브랜치별 로컬 포인터이며 git 추적 대상이 아니다. 로드맵 기반 계획/갱신에서 없으면 `agent-ops/skills/common/_templates/roadmap-current-template.md` 형식으로 생성해 사용하고, 읽기 전용 현지점 확인에서는 생성하지 않는다. +- `current.md`에는 개인별 현재 작업 위치나 완료 상태를 기록하지 않는다. +- `current.md`의 활성 Phase는 `agent-roadmap/phase//PHASE.md`를 가리킨다. +- `current.md`의 활성 Milestone은 `agent-roadmap/phase//milestones/.md`를 가리킨다. +- `current.md`는 `agent-roadmap/archive/**` 경로를 활성 항목으로 포함하지 않는다. +- 요청 내용, 현재 브랜치, 변경 파일, 관련 코드 경로를 보고 가장 관련 있는 활성 Phase와 Milestone 문서를 같은 세션에서 1회 읽는다. +- 활성 Phase 또는 Milestone 밖의 작업이면 이 문서의 Phase 흐름을 확인하고 사용자에게 진행 또는 전환 여부를 확인한다. +- 이 문서는 로드맵 생성/갱신, Phase 전환, Phase 추가/수정, 전체 구조 변경 요청이 있을 때만 읽는다. +- 상세 작업은 각 Milestone 문서의 `기능`으로 관리한다. 검증이 필요한 기능만 같은 Task 안에 `검증:`으로 통합한다. +- `[스케치]` Phase/Milestone은 방향성, 문제의식, 후보 범위, 미정 질문을 기록하는 컨셉 상태이며 구현 계획 생성 대상이 아니다. +- `[스케치]` 항목은 `승격 조건`을 정리해 `[계획]`으로 전환한 뒤 구현 계획을 만든다. +- 모든 기능 Task와 Task 안에 명시된 검증이 충족된 Milestone은 먼저 `[검토중]`으로 두고, 사용자 완료 확인과 archive 승인을 받은 뒤 `[완료]`로 전환한다. +- 완료된 Phase는 `agent-roadmap/archive/phase//PHASE.md`로 이동하고, 하위 Milestone도 같은 archive Phase scaffold 아래에 둔다. +- 진행중 Phase 안에서 완료된 Milestone은 활성 Phase 문서에 짧은 링크를 남기고, 상세 문서는 `agent-roadmap/archive/phase//milestones/`로 이동한다. +- archive `PHASE.md`는 Phase 자체가 완료 또는 폐기될 때만 만들며, 진행중 Phase의 완료 Milestone만 archive된 경우 archive Phase 디렉터리에 `milestones/`만 있을 수 있다. +- `agent-roadmap/archive/**`는 일반 작업에서 읽지 않는다. 과거 완료 내용, 완료 근거, 복원, 비교가 필요한 경우에만 `ROADMAP.md` 또는 `PHASE.md`의 archive 링크를 따라가서 읽는다. +- 아카이브된 Phase/Milestone 문서는 최신 템플릿이나 스킬 규약에 맞춰 재포맷하지 않는다. +- 선택된 Milestone의 `구현 잠금` 섹션이 없거나 상태가 `잠금`이면 코드 구현, `agent-task` 구현 계획 생성, 세부 API/파일 구조 확정을 시작하기 전에 현재 요청에 직접 영향을 주는 `결정 필요` 항목만 확인한다. +- 현재 요청과 직접 관련 없는 미정 항목은 잠금 상태로 남겨도 되며, 기존 구조/도메인 rule/플랫폼 관례로 정할 수 있는 작업은 표준선으로 기록하고 진행할 수 있다. +- Milestone 전체에서 사용자만 결정할 항목이 더 이상 없고 에이전트가 표준선에 따라 실행하면 되는 상태라면 `구현 잠금` 상태를 `해제`로 둔다. diff --git a/agent-ops/skills/common/_templates/skill-template.md b/agent-ops/skills/common/_templates/skill-template.md new file mode 100644 index 0000000..d3acdc2 --- /dev/null +++ b/agent-ops/skills/common/_templates/skill-template.md @@ -0,0 +1,56 @@ +--- +name: +version: 1.0.0 +description: <이 skill이 하는 일을 한 줄로 설명. 트리거 키워드 포함 권장> +--- + +# + +## 목적 + +<이 skill이 해결하는 문제를 1~2문장으로 설명> + +## 언제 호출할지 + +- <이 skill을 호출해야 하는 상황 1> +- <이 skill을 호출해야 하는 상황 2> +- <이 skill을 호출해야 하는 상황 3> + +## 입력 + +- ``: <설명> (필수) +- ``: <설명> (선택) + +## 먼저 확인할 것 + +- [ ] <실행 전 반드시 확인해야 할 조건 1> +- [ ] <실행 전 반드시 확인해야 할 조건 2> + +## 실행 절차 + +1. **<단계명>** + - <세부 행동> + - <세부 행동> + +2. **<단계명>** + - <세부 행동> + +3. **결과 보고** + - <출력할 내용> + +## 실행 결과 검증 + +- [ ] <실행 후 확인해야 할 성공 조건 1> +- [ ] <실행 후 확인해야 할 성공 조건 2> +- 검증 실패 시: <실패 시 취할 행동 — 롤백, 사용자 알림, 재시도 등> + +## 출력 형식 + +``` +<출력 예시> +``` + +## 금지 사항 + +- <절대 하면 안 되는 것> +- <절대 하면 안 되는 것> diff --git a/agent-ops/skills/common/analyze-roadmap-position/SKILL.md b/agent-ops/skills/common/analyze-roadmap-position/SKILL.md new file mode 100644 index 0000000..a8990e6 --- /dev/null +++ b/agent-ops/skills/common/analyze-roadmap-position/SKILL.md @@ -0,0 +1,74 @@ +--- +name: analyze-roadmap-position +version: 1.11.0 +description: "여러 레포를 전환할 때 코드/git 분석 없이 전체 ROADMAP > Phase > Milestone > current 현지점을 링크 달린 breadcrumb와 흐름 목록으로 빠르게 보여주는 읽기 전용 스킬" +--- + +# 로드맵 현지점 + +## 목적 + +여러 레포를 병렬 운용하다가 돌아왔을 때, 현재 작업이 전체 로드맵의 어느 Phase와 Milestone에 있는지 빠르게 보여준다. +기본 출력은 `로드맵 > Phase > Milestone` breadcrumb와 전체 Phase 흐름, 현재 Phase의 Milestone 흐름이다. +코드 진행도 감사, git diff 분석, 테스트 근거 확인, 남은 작업 정밀 판정은 기본 책임이 아니다. + +## 언제 호출할지 + +- 사용자가 "지금 작업이 뭐지?", "현재 작업이 뭐야?", "어디까지 했지?"라고 물을 때 +- 사용자가 레포 전환 직후 로드맵상 현재 좌표를 빠르게 알고 싶어 할 때 +- "로드맵상 현 위치", "현재 마일스톤 위치", "current 기준 breadcrumb"를 요청할 때 +- 구현 시작 전 전체 Roadmap > Phase > Milestone 관계만 확인하면 될 때 + +## 먼저 확인할 것 + +- [ ] `agent-ops/skills/common/_templates/roadmap-position-report-template.md`를 읽어 최신 답변 템플릿 확인 +- [ ] `agent-roadmap/` 디렉터리 존재 여부 확인 +- [ ] 로컬 `agent-roadmap/current.md` 존재 여부 확인 +- [ ] 로드맵이 있으면 `ROADMAP.md`의 `Phase 흐름`을 확인 +- [ ] 로컬 `current.md`의 활성 Phase와 활성 Milestone 이름, 상태, 경로 확인 +- [ ] 활성 Phase 문서의 `Milestone 흐름` 확인 +- [ ] 활성 Milestone 문서의 제목, `목표`, `상태`만 확인 + +## 실행 절차 + +1. `agent-roadmap/` 존재 여부를 확인한다. + - 없으면 로드맵 없음으로 짧게 보고하고 멈춘다. +2. `agent-ops/skills/common/_templates/roadmap-position-report-template.md`를 읽는다. +3. 로컬 `agent-roadmap/current.md`를 확인한다. + - 없으면 로컬 current 없음으로 보고하고, `ROADMAP.md`가 있으면 `Phase 흐름`만 보여준다. +4. `ROADMAP.md`의 `Phase 흐름`을 읽어 전체 Phase 목록을 만든다. + - 각 Phase는 상태, 이름, 링크만 남긴다. + - current의 활성 Phase와 일치하는 항목에 `← 현재` 표시를 붙인다. + - 완료된 Phase가 archive 경로를 가리켜도 링크만 표시하고 archive 문서는 읽지 않는다. +5. current의 활성 Phase 경로를 열고 `Milestone 흐름`을 읽는다. + - 각 Milestone은 상태, 이름, 링크만 남긴다. + - current의 활성 Milestone과 일치하는 항목에 `← 현재 후보` 표시를 붙인다. +6. current의 활성 Milestone 문서를 열고 제목, `목표`, `상태`만 읽는다. + - `기능`, `완료 리뷰`, `범위 제외`, `작업 컨텍스트`는 사용자가 명시적으로 요청한 경우에만 읽는다. +7. 결과를 템플릿 형식 그대로 출력한다. + +## 실행 결과 검증 + +- [ ] `roadmap-position-report-template.md`의 출력 구조를 유지했는가 +- [ ] 로컬 `current.md`, `ROADMAP.md`, 활성 `PHASE.md`, 활성 Milestone의 제목/목표/상태만 기본으로 읽었는가 +- [ ] 완료 또는 archive Phase/Milestone은 링크만 표시하고 archive 문서를 읽지 않았는가 +- [ ] current가 여러 Milestone을 가리키면 모두 `현재 후보`로 표시했는가 +- [ ] 코드 파일, 테스트 파일, `git status`, `git diff`를 기본 동작에서 읽지 않았는가 +- [ ] 로드맵 파일을 수정하지 않았는가 + +## 출력 형식 + +- 템플릿 경로: `agent-ops/skills/common/_templates/roadmap-position-report-template.md` +- 템플릿을 그대로 복사해 placeholder를 채운다. +- 섹션 제목과 필드명을 임의로 번역, 축약, 삭제하지 않는다. +- current가 여러 Milestone을 가리키면 breadcrumb와 Milestone 흐름에 모두 표시한다. +- 현재 후보의 역할 태그는 `선행 스케치`, `다음 구현 계획`, `검토 후보`, `보류 후보`처럼 짧게 쓴다. +- 로드맵이 없는 프로젝트에서는 로드맵 없음으로 짧게 보고하고 템플릿을 억지로 채우지 않는다. + +## 금지 사항 + +- 기본 동작에서 코드 파일, 테스트 파일, `git status`, `git diff`를 읽지 않는다. +- 기본 동작에서 Milestone의 `기능` 체크리스트를 감사하지 않는다. +- 완료 여부, 남은 작업, 코드와 문서의 동기화 상태를 evidence 기반으로 판정하지 않는다. +- 사용자가 명시하지 않은 상태에서 `ROADMAP.md`, 로컬 `current.md`, Phase, Milestone 문서를 수정하지 않는다. +- 사용자가 과거 기록 확인을 명시하지 않으면 `agent-roadmap/archive/**`를 읽지 않는다. diff --git a/agent-ops/skills/common/check-roadmap-dependency/SKILL.md b/agent-ops/skills/common/check-roadmap-dependency/SKILL.md new file mode 100644 index 0000000..d11ae00 --- /dev/null +++ b/agent-ops/skills/common/check-roadmap-dependency/SKILL.md @@ -0,0 +1,91 @@ +--- +name: check-roadmap-dependency +version: 1.2.0 +description: roadmap dependency lock 상태를 확인한다. 사용자가 roadmap-dependency-checker.sh, locks.yaml, 외부 의존 잠금 충족 여부, unlock-ready 판별, 프로젝트 간 잠금 해제 조건 확인을 요청할 때 사용하며, 현재 Milestone이 locked 쪽인지 rely-on.target 쪽인지 양방향으로 판별한다. +--- + +# check-roadmap-dependency + +## 목적 + +`.agent-roadmap-sync/locks.yaml`에서 특정 lock `id`의 `locked`, `rely-on.target`, `rely-on.status`가 유효한지 확인하고, 모든 `rely-on.status`가 `enable`인지 판별한다. +lock id가 없으면 현재 Milestone identity가 `locked`에 있는지, `rely-on.target`에 있는지 모두 찾아 양방향으로 보고한다. +Milestone identity 역검색과 개별 lock 판별은 `agent-ops/bin/roadmap-dependency-checker.sh`에 맡기고, 스킬은 입력 확정, 실행, 결과 해석만 담당한다. + +## 언제 호출할지 + +- 사용자가 특정 roadmap lock id의 의존 조건 충족 여부를 확인할 때 +- runtime에서 unlock-ready 트리거 전 `locks.yaml` 판별 절차를 확인할 때 +- `.agent-roadmap-sync/locks.yaml`의 `rely-on.status` 기반 true/false 확인을 요청할 때 +- 사용자가 현재 Milestone이 다른 Milestone을 잠그는지 또는 다른 Milestone에 의해 잠겼는지 확인할 때 + +## 입력 + +- `lock-id`: 확인할 lock entry id. 예: `oto:oto-agent-registration`. 명시되면 이 id만 판별한다 (선택) +- `target-milestone`: 확인할 Milestone path 또는 `:agent-roadmap/phase/.../milestones/.md`. 없으면 현재 프로젝트 로컬 `current.md`의 활성 Milestone 단일 후보를 사용한다 (선택) +- `direction`: `locked` / `rely-on` / `both`. 기본값은 `both`다. `locked`는 대상 Milestone이 잠긴 entry만, `rely-on`은 대상 Milestone을 선행 조건으로 삼는 entry만 확인한다 (선택) +- `locks-file`: 기본값은 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`이며, 다른 파일을 검사할 때만 지정한다 (선택) + +## 먼저 확인할 것 + +- [ ] `agent-ops/bin/roadmap-dependency-checker.sh`가 있는지 확인한다. +- [ ] `lock-id`가 명시되었거나 `target-milestone` 또는 현재 프로젝트 로컬 `current.md`의 활성 Milestone 하나로 Milestone identity를 추론 가능한지 확인한다. +- [ ] 기본 경로 또는 지정된 `locks-file`을 읽을 수 있는지 확인한다. + +## 실행 절차 + +1. **입력 확정** + - 사용자 요청에서 `lock-id`를 추출한다. + - `locks-file`이 명시되지 않았으면 프로젝트 상위 `.agent-roadmap-sync/locks.yaml`을 사용한다. + - `locks-file`이 없거나 읽을 수 없으면 결과를 `error`로 보고한다. 이를 미충족 의존성으로 해석하지 않는다. + - `lock-id`가 있으면 그 id만 판별한다. + - `lock-id`가 없으면 `target-milestone` 또는 현재 프로젝트 로컬 `agent-roadmap/current.md`의 활성 Milestone 단일 후보로 Milestone identity를 만든다. + - Milestone identity는 `:agent-roadmap/phase//milestones/.md` 형식이다. + - `target-milestone`이 `agent-roadmap/...` 상대 경로이면 현재 프로젝트명을 prefix로 붙인다. + - `target-milestone`이 workspace 하위 절대/상대 경로이면 workspace 바로 아래 디렉터리명을 project로 삼고, 그 뒤 `agent-roadmap/...` 경로를 붙인다. + - `target-milestone`이 archive 경로이면 일반 의존 판별 후보로 쓰지 말고 사용자에게 활성 Milestone 선택을 요청한다. + - 활성 Milestone 후보가 없거나 둘 이상이면 추정하지 말고 사용자에게 lock id 또는 Milestone 선택을 요청한다. + - `lock-id`가 없으면 `agent-ops/bin/roadmap-dependency-checker.sh --find-milestone "" "" ""`로 Milestone identity와 일치하는 entry를 찾는다. + - find 출력의 `locked:`는 locked entries로, `rely-on:`는 rely-on entries로 분류한다. + - find 출력이 `none`이면 결과를 `error`가 아니라 `no-lock`으로 보고한다. + +2. **판별 실행** + - 확인 대상 lock id마다 `agent-ops/bin/roadmap-dependency-checker.sh "" ""`를 실행한다. + - `locked` 방향의 결과는 대상 Milestone의 잠금 해제 조건 충족 여부다. + - `rely-on` 방향의 결과는 대상 Milestone이 완료될 때 영향을 받는 downstream lock의 현재 충족 여부다. + +3. **결과 해석** + - stdout `true`와 exit code `0`은 모든 의존 조건 충족으로 보고한다. + - stdout `false`와 exit code `1`은 아직 충족되지 않은 의존 조건이 있음을 보고한다. + - exit code `2`는 설정 오류, 입력 오류, 필수 필드 누락, 파싱 오류로 보고한다. + - `rely-on` 방향에서 대상 Milestone이 `[검토중]` 또는 `[완료]`인데 status가 `disable`이면 `update-roadmap` 동기화가 필요한 stale lock으로 보고한다. 이 스킬은 status를 바꾸지 않는다. + +## 실행 결과 검증 + +- [ ] 각 checker 실행의 stdout이 `true` 또는 `false` 하나로만 출력되었는가 +- [ ] exit code `0`, `1`, `2` 의미를 구분해 보고했는가 +- [ ] exit code `2`를 unlock 대기 상태로 해석하지 않았는가 +- [ ] lock id가 없을 때 대상 Milestone을 `locked`와 `rely-on.target` 양쪽에서 찾았는가 +- [ ] `locks.yaml`이 없는 경우 `false`가 아니라 `error`로 보고했는가 +- [ ] 이 스킬이 `locks.yaml`이나 roadmap 문서를 수정하지 않았는가 +- 검증 실패 시: 오류 메시지와 사용한 `lock-id`, `locks-file` 경로를 보고하고 멈춘다. + +## 출력 형식 + +```markdown +## 의존성 판별 결과 + +- target-milestone: `` +- direction: `` +- locks-file: `` +- 결과: `` +- locked entries: `` +- rely-on entries: `` +- 의미: <모든 의존 조건 충족 | 미충족 의존 조건 있음 | 설정/입력/필수 필드/파싱 오류 | 대상 Milestone 관련 lock 없음> +``` + +## 금지 사항 + +- `locks.yaml`의 `status`를 변경하지 않는다. +- roadmap Milestone의 `구현 잠금` 상태를 변경하지 않는다. +- `false`와 설정 오류를 같은 상태로 보고하지 않는다. diff --git a/agent-ops/skills/common/code-review/SKILL.md b/agent-ops/skills/common/code-review/SKILL.md new file mode 100644 index 0000000..ba444f2 --- /dev/null +++ b/agent-ops/skills/common/code-review/SKILL.md @@ -0,0 +1,455 @@ +--- +name: code-review +description: Use for active task review requests such as 리뷰 진행해, 리뷰해줘, 코드 리뷰해줘, code review, CODE_REVIEW.md, USER_REVIEW.md resolution, implementation-time 사용자 리뷰 요청, or 리뷰 루프. Review the active PLAN/CODE_REVIEW pair, append PASS/WARN/FAIL, archive both active files, and create the required next state: PASS writes complete.log and moves the task to archive; WARN/FAIL writes follow-up PLAN/CODE_REVIEW files unless the user-review gate requires USER_REVIEW.md; user review resolution that closes the task writes complete.log before archive. Never stop after verdict append or user-review closure without writing the required next-state artifact. +--- + +# Code Review + +## Purpose + +Review the implementation phase of the plan-code-review loop: + +```text +plan skill -> implementation -> code-review skill + ^ | + +----- issues found: new routed plan/review files +``` + +Implementation may stop early by filling the active review stub's `사용자 리뷰 요청` section from `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`. This is the only implementation-time user-decision path: implementation must not ask the user directly, present choices in chat, or call a user-input tool such as `request_user_input`. Code-review validates that request and, when justified, writes `USER_REVIEW.md` from `agent-ops/skills/common/code-review/templates/user-review-template.md`. + +## Core Loop Rules + +- Trigger: Korean or English active-task review requests, including `리뷰 진행해` and `리뷰해줘`, must use this skill when an active `CODE_REVIEW-*-G??.md` or `USER_REVIEW.md` exists under `agent-task/*/` or `agent-task/*/*/`, excluding `agent-task/archive/**`. +- Finalize every selected active state: for `CODE_REVIEW-*-G??.md`, append one verdict, archive the active review and plan files, then create exactly one next state; for `USER_REVIEW.md` completion, update the stop state, write `complete.log`, and archive the task. Do not report before finalization is complete. +- Next state: `PASS` writes `complete.log` and moves the task under `agent-task/archive/YYYY/MM/`; if the task group is `m-`, report completion metadata for the runtime event. `WARN` or `FAIL` normally writes the next active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md`; if the user-review gate triggers, write `USER_REVIEW.md` instead. A completed `USER_REVIEW.md` uses the same terminal `complete.log` and archive path as `PASS`. +- A filled implementation-owned `사용자 리뷰 요청` is a review input, not a next state by itself. If it is a genuine user-only decision, user-owned external environment prerequisite, or scope conflict, the user-review gate triggers and this skill writes `USER_REVIEW.md`; if it is vague, unsupported, repo-fixable, or only missing evidence that a follow-up agent can produce by rerunning commands or collecting artifacts, write a normal WARN/FAIL follow-up plan instead. +- Do not replace `USER_REVIEW.md` with an inline user question. When the user-review gate triggers, write the file-based stop state and report its path. +- Do not ask for confirmation before WARN/FAIL follow-up files. If the user-review gate triggers, write `USER_REVIEW.md`; otherwise, if details are uncertain, write the smallest concrete follow-up plan with file references and verification commands. +- Recovery: if a prior turn appended a verdict without archive or next-state files, or resolved `USER_REVIEW.md` without `complete.log`, resume at the matching finalization step and finish the missing artifacts first. + +## User Review Gate + +`USER_REVIEW.md` is a loop stop state for loop-limit exhaustion or user-owned blockers. Default to a normal WARN/FAIL follow-up; the gate requires positive evidence that another implementation/review agent cannot resolve the blocker without user action. + +- Compute `review-number` as `count(agent-task/{task_name}/code_review_*.log) + 1` before archiving the active review. +- If `review-number >= 5` and the verdict is `WARN` or `FAIL`, stop the automatic loop and write `USER_REVIEW.md`. +- If `review-number == 1`, the verdict is `FAIL`, and the blocking issue is a test environment problem that requires user action rather than repo-owned setup or evidence a follow-up agent can produce by rerunning commands or collecting artifacts, stop the automatic loop and write `USER_REVIEW.md`. +- If the active review file has a filled `사용자 리뷰 요청` with `상태` other than `없음`, and the blocker is a user-only decision, user-owned external environment prerequisite, or scope conflict that the repository and a capable next agent cannot safely resolve by code changes or verification reruns, stop the automatic loop and write `USER_REVIEW.md`. +- If the active review file records a decision the implementer would otherwise ask as chat choices, treat it as an `implementation-blocked` user-review candidate when it has the required evidence and resume condition; do not ask that choice inline during review. +- Treat a first-review failure as a test environment problem only with evidence from an attempted command: missing SDK/tool/runtime, unavailable external service, absent secret/credential, unsupported OS/device/interactive environment, or another prerequisite outside the repository. Record the exact command and stdout/stderr. Before claiming a tool is missing, require `command -v ` or the project-equivalent check. Missing or non-verbatim verification evidence is a verification-trust failure, not a test environment problem. +- Treat an implementation-owned `사용자 리뷰 요청` as valid only with specific decision questions, evidence, attempted commands/output or a reason commands could not run, and clear resume conditions. The not-run reason must identify user-owned action or an external prerequisite; agent execution limits, missing handoff evidence, or incomplete verification records become normal WARN/FAIL follow-up plans when a capable follow-up lane can produce the evidence. +- Do not classify repo-owned setup bugs or verification evidence gaps as environment problems. If a manifest, bootstrap script, test fixture, documented project entrypoint, or follow-up agent can provide the dependency or evidence, write a normal follow-up plan instead. +- `USER_REVIEW.md` is created from `agent-ops/skills/common/code-review/templates/user-review-template.md`, filled with the archived loop history, current archived plan/review paths, verdict, loop count, blocking evidence, and user decision options. + +## User Review Resolution + +When an active `USER_REVIEW.md` exists and the user decision closes the task as complete/PASS, finalization is still owned by this skill. + +- Read `USER_REVIEW.md`, archived `plan_*.log`, and archived `code_review_*.log` in that task directory. +- Verify the user decision and any follow-up evidence are sufficient to close the task. If a new implementation plan is needed instead, do not close; route back to the plan skill, which archives `USER_REVIEW.md` to `user_review_N.log` before writing a new plan. +- Update `USER_REVIEW.md` in place to show a resolved state, final verdict, loop history, fulfilled decision items, and the evidence that closed the stop state. +- Write `complete.log` from `agent-ops/skills/common/code-review/templates/complete-log-template.md` before moving or archiving the task directory. Include both the original archived review verdict and the user-review resolution line in `루프 이력`. +- Then apply the same task-directory archive move and `m-` PASS completion metadata rules as a normal `PASS`. +- Do not leave an active task directory that contains `USER_REVIEW.md` and `*.log` files but no `complete.log` after the user has completed and approved the task. + +## Workflow Contract + +Active work must live under an active task directory using routed filenames. This is the state protocol shared with the plan skill. + +Task path terms: + +- `{task_group}` is the top-level work category under `agent-task/`. Normal task groups use a short snake_case name such as `refactoring`. +- Milestone-linked work uses the reserved task group form `m-`, where `` is the active Milestone filename without `.md`. +- `{subtask_dir}` is used only for split work and follows the indexed directory naming contract, such as `01_core` or `02+01_db`. +- `{subtask_name}` is the short snake_case name after the index or dependency prefix inside `{subtask_dir}`. +- `{task_name}` in headers and templates means the active task path relative to `agent-task/`: either `{task_group}` for a single-plan task or `{task_group}/{subtask_dir}` for a split subtask. +- A single-plan task stores active files directly under `agent-task/{task_group}/`. +- Split work stores active files under `agent-task/{task_group}/{subtask_dir}/`; the parent `agent-task/{task_group}/` is only the grouping folder and must not contain active plan/review files. + +Filename rules: + +- Plan file: `PLAN-{build_lane}-GNN.md` +- Review file: `CODE_REVIEW-{review_lane}-GNN.md` +- `{lane}` is only `local` or `cloud`; never put model names in filenames. +- `GNN` is a two-digit capability grade from `G01` to `G10`; runtime maps lane+grade to current models externally. + +Multi-plan runtime contract: + +- Multi-plan work is represented as multiple subtask directories under one shared `{task_group}`. Each subtask directory owns exactly one normal active plan file and one normal active review file. +- Multi-plan subtask directory names encode runtime scheduling metadata: + - `NN_{subtask_name}` has no runtime dependencies. + - `NN+PP[,QQ...]_{subtask_name}` depends on the listed earlier task indices. +- Subtask directory names are the runtime dependency source of truth. Preserve them verbatim; do not normalize, reinterpret, infer extra dependencies from numeric order, or choose execution order by agent judgment. +- If the user/runtime names a task group, task path, or subtask directory that identifies exactly one active review file, review that directory even when other active review files exist. + +Milestone task group contract: + +- `agent-task/m-/` is reserved for Milestone-linked work created by the plan skill. +- Do not treat normal task groups that do not start with `m-` as runtime milestone completion targets. +- For a selected task path, parse only the first path segment as `{task_group}`. If it matches `^m-[a-z0-9][a-z0-9-]*$`, strip `m-` to get ``. +- Do not read or modify `agent-roadmap/**` for milestone routing during code-review finalization. +- Do not call `update-roadmap` from this skill. The runtime consumes the PASS completion event, checks current state, resolves the active Milestone, and calls `update-roadmap` if needed. +- For `m-` PASS tasks, report the original active task path, final archive path, complete log path, task group, and milestone slug so the runtime has deterministic event inputs. + +Review routing rules: + +- Route reviews so a normal mixed backlog can use both lanes substantially. This is not a per-review quota; neither `local` nor `cloud` is the default catch-all. +- `local`: first-pass and review-detectable work, including multi-file or API/call-site reviews when scope and required context are bounded and verification can be rerun or reasoned about locally. +- `local-G01` through `local-G10` and `cloud-G01` through `cloud-G10` are valid. Do not treat high grade alone as an implicit cloud route; context volume is separate lane evidence. +- Repair non-behavioral review artifact drift during review instead of failing solely for it when implementation correctness, tests, and contracts remain judgeable. +- `cloud`: reviews requiring stronger-than-local judgment, larger-than-local context, or evidence recovery: weak verification that review cannot compensate for, security/auth, storage/migration, concurrency, protocol/schema, broad cross-domain behavior with hard-to-review failure modes, repeated non-trivial Required issues, or untrusted verification evidence. +- `cloud-G07` or higher is mandatory only when terminal-agent or external interactive behavior is central and cannot be made deterministic for local review, or when the review requires more source/test/diff/log context than local can hold without omitting material evidence: shell/CLI workflow implementation, bin script orchestration, process control, stdout/stderr parsing, exit-status contracts, long-running command diagnosis, terminal benchmark-style tasks, real bin/smoke/integration failures after unit tests passed, TUI/PTY/browser/screen repaint/cursor stream behavior, rerun-mismatched verification trust failures, or very large cross-module/cross-domain changes that cannot be safely split for review. + +Directory states: + +| State | Meaning | +|-------|---------| +| `PLAN-*-G??.md` + unfilled `CODE_REVIEW-*-G??.md` stub/placeholders | Implementation is not judgeable; review should fail completeness if invoked | +| `PLAN-*-G??.md` + `CODE_REVIEW-*-G??.md` with filled `사용자 리뷰 요청` | Implementation claims a user-only blocker; review validates the request and writes `USER_REVIEW.md` only if justified | +| `PLAN-*-G??.md` + filled `CODE_REVIEW-*-G??.md` | Ready for code-review skill | +| `complete.log` + `*.log` files | Task complete (PASS or user-review-resolved PASS), before final task-directory archive move | +| `USER_REVIEW.md` + `*.log` files | Automatic loop stopped; user decision is required before creating another plan | +| `agent-task/archive/YYYY/MM/{task_name}/complete.log` + `*.log` files | Archived completed task path (PASS or user-review-resolved PASS); not active | +| Only `*.log` files (no `complete.log`) | Task terminated mid-loop or abandoned | + +The implementing agent never archives or deletes active files; archiving is this skill's responsibility. + +## Step 1 - Find Active Task + +Find active review files with both globs, excluding `agent-task/archive/**`: + +- `agent-task/*/CODE_REVIEW-*-G??.md` +- `agent-task/*/*/CODE_REVIEW-*-G??.md` + +Also note active user-review stops, excluding `agent-task/archive/**`: + +- `agent-task/*/USER_REVIEW.md` +- `agent-task/*/*/USER_REVIEW.md` + +Classify the combined set of active `CODE_REVIEW-*-G??.md` and `USER_REVIEW.md` paths. Apply the first matching row: + +| Result | Action | +|--------|--------| +| Exactly one active path and it is `CODE_REVIEW-*-G??.md` | Review that task; exactly one `PLAN-*-G??.md` is expected beside it. | +| Exactly one active path and it is `USER_REVIEW.md`, with a user-supplied completion/resolution decision | Perform User Review Resolution for that task. | +| One or more active paths and every active path is `USER_REVIEW.md`, with no completion/resolution decision | Report that user decision is required and list the paths. | +| No active paths | Stop and report that no active review exists. | +| Multiple active paths | If the user/runtime named a task group, task path, or subtask directory that identifies exactly one active path, use that directory. Otherwise list paths and ask which task to review or resolve; do not choose by agent judgment. | + +If a selected task directory contains both `USER_REVIEW.md` and active `PLAN-*-G??.md` or `CODE_REVIEW-*-G??.md`, report an inconsistent loop state instead of overwriting either state. + +## Step 2 - Load Context + +Count `agent-task/{task_name}/code_review_*.log` in the selected active task directory: + +- `0`: first review. Read the active review file, active plan file, every planned source file, related tests, and files importing/imported by changed files up to 2 levels deep. +- `>=1`: follow-up review. Start with `git diff`, `git diff --cached`, and `git log --oneline -5`, then expand to related callers, implementers, tests, and any planned files missing from the diff. + +The diff is the starting point, not the boundary. Follow behavior and API connections far enough to judge correctness. + +## Step 3 - Pre-Review Checklist + +Before writing the verdict: + +- Compare actual source files against every planned checklist item. +- Compare the plan `구현 체크리스트` and review stub `구현 체크리스트`; repair non-behavioral drift when implementation remains judgeable. +- Directly repair obvious non-behavioral source nits when safe: typos, stale comments, docs, or formatting only, with no behavior/test/API contract change. +- If a checklist item contains integrated verification for a feature, treat that feature item as incomplete until both implementation evidence and the matching verification output are present. Do not accept a separate unchecked completion-criteria item as a substitute. +- Confirm the implementation marked the matching checklist items in the active review file, including the final mandatory `CODE_REVIEW-*-G??.md` completion item; repair clear artifact drift when evidence supports completion. +- Treat review artifact gaps as failures only when they prevent judging implementation correctness, tests, contracts, or verification trust. +- Read the `사용자 리뷰 요청` section. If `상태` is not `없음`, validate the exact decision needed, blocker evidence, command output or not-run reason, why automatic follow-up cannot resolve it, and resume condition before deciding whether the user-review gate triggers. +- Grep renamed/removed symbols for stale references. +- Confirm every required test exists, name matches, and assertions are meaningful. +- Cross-check claimed verification output in the active review file against actual code and project commands. +- For follow-up reviews, compare diff against the plan and scan for unplanned changes, debug prints, dead code, TODOs, formatting-only noise, and unrelated edits. + +## Step 4 - Append Verdict + +Append `코드리뷰 결과` to the active `CODE_REVIEW-*-G??.md`. + +Required fields: + +- `종합 판정`: exactly `PASS`, `WARN`, or `FAIL`. +- `차원별 평가`: Pass/Warn/Fail for correctness, completeness, test coverage, API contract, code quality, plan deviation, verification trust. +- `발견된 문제`: `없음`, or bullets using `Required`, `Suggested`, or `Nit` with `file:line` and a concrete fix. +- `다음 단계`: keep only the matching PASS, WARN/FAIL follow-up, or USER_REVIEW line. + +Do not check archive/next-state items in `코드리뷰 전용 체크리스트` during Step 4. Complete the applicable dedicated checklist items in the archived `code_review_*.log` during Step 7, after archive, next-state writes, and PASS task-directory moves are done. + +Severity semantics: + +| Verdict | Meaning | Follow-up plan | +|---------|---------|----------------| +| `PASS` | No Required/Suggested issues. Nit-only findings may still PASS. | No | +| `WARN` | One or more Suggested issues, zero Required. | Yes, unless the user-review gate triggers | +| `FAIL` | One or more Required issues. | Yes, unless the user-review gate triggers | + +Issue severity: + +- `Required`: correctness, API contract, missing required test, missing integrated verification, plan-completeness issue, or review artifact gaps that prevent judging implementation quality. +- `Suggested`: useful improvement that should enter the loop but does not block correctness. +- `Nit`: tiny cleanup; directly repair obvious non-behavioral cases when safe, otherwise record without forcing WARN. + +Verdict consistency: + +- `PASS` requires all dimensions to be Pass and no Required/Suggested issues. Nit-only findings may still PASS only when every dimension remains Pass. +- Any Fail dimension or any Required issue forces `FAIL`. +- Any Warn dimension or any Suggested issue forces `WARN`, unless the only findings are explicitly Nit and every dimension remains Pass. + +## Step 5 - Archive Active Files + +Archive is mandatory for `PASS`, `WARN`, and `FAIL`. Before the archive order, ensure `.gitignore` has the Agent-Ops managed gitignore block for task artifacts before writing `*.log` outputs. Prefer `source agent-ops/bin/ai-ignore.sh && agent_ops_ensure_gitignore_task_artifact_block .gitignore`; if the helper is unavailable, add or update a block containing `!agent-task/`, `!agent-task/**/`, `!agent-task/**/*.md`, `!agent-task/**/*.log`, and `agent-roadmap/current.md`. + +Archive order is fixed: + +1. Count existing `code_review_*.log` as `N`; rename `CODE_REVIEW-{review_lane}-GNN.md` to `code_review_{review_lane}_GNN_N.log`. +2. Count existing `plan_*.log` as `M`; rename `PLAN-{build_lane}-GNN.md` to `plan_{build_lane}_GNN_M.log`. + +After archiving, neither active `.md` file remains unless Step 6 writes a follow-up plan/review or `USER_REVIEW.md`. + +## Step 6 - Post-Review Actions + +For `PASS`, write `agent-task/{task_name}/complete.log` before reporting. If a `USER_REVIEW.md` stop is resolved as complete/PASS, write the same `complete.log` before archiving that task. + +Complete log template: + +- Template path: `agent-ops/skills/common/code-review/templates/complete-log-template.md` +- Copy the template's section order and fill every placeholder from the archived plan/review logs and final verdict. +- Do not leave placeholders in `complete.log`. +- If the task did not close through `USER_REVIEW.md`, remove the optional user-review row from the `루프 이력` table. +- If the archived plan or review log contains `Roadmap Targets`, copy it into `complete.log` as `Roadmap Completion`. Include the Milestone path, completed Task ids, archived plan/review log paths, and verification evidence. If there is no `Roadmap Targets` section, remove the optional `Roadmap Completion` template section entirely and do not invent roadmap targets. +- Use `없음` for empty `잔여 Nit` or `후속 작업`. +- A PASS `complete.log` must not contain unresolved Required or Suggested issues. Nit-only leftovers may be recorded under `잔여 Nit`. + +For `WARN` or `FAIL`, apply the user-review gate before writing follow-up files. + +If the user-review gate triggers: + +- Write `agent-task/{task_name}/USER_REVIEW.md` from `agent-ops/skills/common/code-review/templates/user-review-template.md`. +- Fill every placeholder. Use `loop-limit`, `environment-blocked`, or `implementation-blocked` as the reason type. +- Include all available archived loop history in chronological order, the current archived plan/review log paths, the final verdict, review number, issue summary, blocking evidence, and the exact user decision needed. +- Do not write a new active `PLAN-{build_lane}-GNN.md` or `CODE_REVIEW-{review_lane}-GNN.md`. +- Do not write `complete.log`. +- If the task group is `m-`, report that the milestone task is blocked on user review; do not emit PASS completion metadata and do not call `update-roadmap`. + +If the user-review gate does not trigger, write new routed plan/review files using the plan skill format: + +- New plan number is the count of `plan_*.log` after archive. +- Header tag is `REVIEW_`. +- If the selected task group is `m-`, write the follow-up plan/review under the same `m-` task group. +- Base the follow-up scope directly on the archived review findings. Keep it narrow and actionable. +- Choose lane/grade again; preserve the prior route when it was adequate. Raise `local-GNN` for explicit, deterministic, review-detectable follow-ups, but move `local -> cloud` when the failure exposes reasoning, risk, evidence, or context-volume gaps that local review should not own. +- If the finding is only missing or untrusted verification evidence that an appropriate follow-up lane can produce by rerunning commands or collecting artifacts, do not write `USER_REVIEW.md` solely for that reason. Write a follow-up plan that reruns the exact commands in an appropriate lane and records actual stdout/stderr. +- If follow-up verification involves mobile/UI hangs, timeouts, or no progress for 2 minutes, stop blind retries; require focused rerun output plus screenshot/window/UI-tree evidence path, or the exact reason capture is impossible. +- Before choosing the follow-up route, apply this escalation gate: + - If the archived plan was `local-*` and the verdict is `FAIL` for correctness, completeness, test coverage, or verification trust, keep the follow-up on `local` with a higher grade when the issue is explicit, deterministic, and review-detectable; move to `cloud` only when the failure shows local capability or evidence trust is likely insufficient. + - If the follow-up work is terminal-agent work that cannot be reduced to deterministic commands and evidence, use `cloud-G07` or higher. + - If unit tests passed but a real bin/smoke/integration command failed and local follow-up cannot isolate the failure with deterministic evidence, use `cloud-G07` or higher. + - If the task depends on interactive TUI/PTY/browser/external CLI behavior, screen repaint/cursor stream parsing, or live command-palette state that cannot be captured deterministically, use `cloud-G07` or higher. + - If the follow-up requires more source/test/diff/log context than local can hold without dropping material evidence, or the failure spans broad cross-module/cross-domain context that cannot be safely split, use `cloud-G07` or higher. + - If verification evidence is missing, reconstructed, stale, untrusted, or mismatched on rerun, use a local follow-up only when exact commands and actual stdout/stderr capture are enough; use `cloud-G07` or higher when evidence trust recovery needs stronger diagnosis. +- `FAIL`: one plan item per Required issue. +- `WARN`: one grouped plan item for Suggested issues, plus related Nit issues if useful. +- Each plan item needs problem, solution with before/after when non-trivial, checklist, test decision, intermediate verification. +- The follow-up plan and review stub must contain matching `구현 체크리스트` item text/order, including the final mandatory `CODE_REVIEW-*-G??.md` completion item. +- The follow-up plan's implementer instructions and review stub must forbid direct user prompts and include the implementation-time `사용자 리뷰 요청` stop path so the next implementer can act without reading the plan skill. +- Copy `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` into the follow-up review stub's `사용자 리뷰 요청` section. +- If the archived plan had `Roadmap Targets` and the follow-up still works toward the same Milestone Task completion, copy that section into the follow-up plan and review stub. If the follow-up intentionally no longer claims roadmap Task completion, omit the section and explain why in the follow-up plan `범위 결정 근거`. + +Routed review stub template (fill `{…}` placeholders; everything else is fixed and must not be changed by the implementing agent): + +```markdown + + +# Code Review Reference - {TAG} + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves. +> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date={YYYY-MM-DD} +task={task_name}, plan={N}, tag={TAG} + +{copy the follow-up plan's Roadmap Targets section exactly if present; omit this block if the follow-up plan has no Roadmap Targets} + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-{review_lane}-GNN.md` → `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [{TAG}-1] {item description} | [ ] | +| [{TAG}-2] {item description} | [ ] | + +## 구현 체크리스트 + +{copy the follow-up plan's 구현 체크리스트 items exactly, preserving order and checkbox text} + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. +- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +{pre-filled from plan — one bullet per review focus area} + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### {TAG}-1 중간 검증 +``` +$ {verification command from plan} +(output) +``` + +### 최종 검증 +``` +$ {final verification command from plan} +(output) +``` + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** +> If anything is blank, go back and fill it in before saving this file. +> Leave review-agent-only sections unchanged. +``` + +Sections and their ownership: + +| 섹션 | 소유자 | 설명 | +|------|--------|------| +| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 | +| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 | +| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` → `[x]` 체크만 구현 에이전트가 수행 | +| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` → `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | +| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 | +| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 진행에 사용자 입력이 필요하지 않으면 `상태: 없음` 유지; 구현 중 직접 질문은 금지; 필요하면 결정 항목, 근거, 명령 출력, 자동 후속 불가 이유, 재개 조건을 기록 | +| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 | +| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 | +| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 | + +## Step 7 - Complete Review-Only Checklist, Move PASS Task, And Report + +After Step 6: + +- If verdict is `PASS`, determine archive month from the current completion date as `YYYY/MM`, create the needed archive parent directories, then move the selected active task directory `agent-task/{task_name}/` to `agent-task/archive/YYYY/MM/{task_name}/`. For split work, the selected active task directory is the subtask directory, and the archive must preserve the task group path, e.g. `agent-task/refactoring/01_core/` moves to `agent-task/archive/YYYY/MM/refactoring/01_core/`. +- Do not overwrite an existing archive directory. If `agent-task/archive/YYYY/MM/{task_name}/` already exists, append the next numeric suffix to the final path segment: single-plan `agent-task/archive/YYYY/MM/{task_group}_1/`, split-plan `agent-task/archive/YYYY/MM/{task_group}/{subtask_dir}_1/`, and so on. +- After moving a split subtask, remove the active parent `agent-task/{task_group}/` only if it is empty. If sibling subtask directories or other files remain, leave the parent task group in place. +- If verdict is `PASS` and `{task_group}` matches `m-`, do not resolve the roadmap target and do not call `update-roadmap`. Report completion event metadata after the task archive move: `origin-task=agent-task/{task_name}` from the original active task path, `task-group={task_group}`, `milestone-slug=`, final archive path, `complete.log` path, archived plan/review log paths, and `roadmap-completion=`. +- The runtime consumes that completion event, checks current state, and calls `update-roadmap` if needed. `update-roadmap` only checks Milestone Task ids when `complete.log` contains `Roadmap Completion`; if the section is absent, roadmap Task completion is a no-op even for `m-*` task groups. +- `WARN` and `FAIL` do not update the roadmap Milestone; the follow-up plan remains under the same `m-` task group when the original task was Milestone-linked. +- `USER_REVIEW` does not update the roadmap Milestone and does not produce PASS completion metadata. Keep the active task directory in place with `USER_REVIEW.md` and archived plan/review logs until the user decides how to resume, replan, defer, or abandon the task. +- If `USER_REVIEW.md` is later resolved as complete/PASS by user decision and evidence, write `complete.log`, move the task directory to archive, and report `m-*` PASS completion metadata just like a normal `PASS`. +- For `PASS`, open the moved `agent-task/archive/YYYY/MM/{final_task_name}/code_review_{review_lane}_GNN_N.log`, where `{final_task_name}` is the archived task path, including `{task_group}/` for split work. +- For user-review-resolved PASS, confirm the moved archive contains the resolved `USER_REVIEW.md`, `complete.log`, and the existing archived `plan_*.log` / `code_review_*.log`; do not recreate an active review file only to add a new checklist item. +- For `WARN` or `FAIL`, open `agent-task/{task_name}/code_review_{review_lane}_GNN_N.log`. +- Run `git check-ignore -q --` on the generated task artifacts (`plan_*.log`, `code_review_*.log`, `user_review_*.log` when present, `complete.log` when present, and active follow-up `.md` files). If any are ignored, apply the Agent-Ops managed gitignore block and re-check before reporting. +- Check every applicable item in `코드리뷰 전용 체크리스트`; leave mutually exclusive verdict items unchecked. +- If any applicable item cannot be checked, finish the missing archive, `complete.log`, task-directory move, follow-up plan/review write, or `USER_REVIEW.md` write first. +- Do not recreate an active review file just to update this checklist; update the archived `code_review_*.log`. +- Only report after the archived review log has the verdict, applicable checked review-only checklist, required next-state files, for `PASS` or user-review-resolved PASS the final task archive move, for `m-*` PASS tasks the completion event metadata, and for unresolved `USER_REVIEW` the filled `USER_REVIEW.md`. + +Report Required/Suggested counts, archive names, the final task archive path for `PASS`, the new plan path for normal `WARN`/`FAIL`, the `USER_REVIEW.md` path for user review stops, and any `m-*` runtime completion event metadata. + +## Review Dimensions + +| Dimension | Check | +|-----------|-------| +| Correctness | Logic, edge cases, concurrency, errors | +| Completeness | Planned implementation/verification items are done; review artifact drift is repaired when judgeable | +| Test coverage | Required tests present and meaningful | +| API contract | Call sites, compatibility, docs | +| Code quality | No debug prints, dead code, leftover TODOs | +| Plan deviation | Deviations justified, no unrelated risk | +| Verification trust | Reported output matches actual code | + +## Quality Rules + +- Lead with findings; use specific `file:line`. +- Provide a concrete fix for every Required issue. +- Name exact stale symbols or missing tests. +- Do not write vague praise or style opinions without a rule. +- Every dimension gets Pass/Warn/Fail. +- For follow-up plans about verification trust, specify deterministic commands, for example `rg --sort path`, and forbid repo-local tool artifacts. + +## Final Checklist + +- `code_review_{review_lane}_GNN_N.log` exists with verdict appended. +- `plan_{build_lane}_GNN_M.log` exists. +- `.gitignore` unignores `agent-task/**/*.md` and `agent-task/**/*.log` and ignores local `agent-roadmap/current.md`; generated task artifacts are not ignored by `git check-ignore`. +- No active `.md` files remain after PASS or user-review-resolved PASS. +- PASS or user-review-resolved PASS: `complete.log` written from `agent-ops/skills/common/code-review/templates/complete-log-template.md`, then task directory moved under `agent-task/archive/YYYY/MM/` with task-group path preserved for split work. +- PASS milestone task group: `m-` completion event metadata was reported for runtime; roadmap was not modified by code-review. +- PASS with `Roadmap Targets`: `complete.log` contains `Roadmap Completion` with Milestone path, Task ids, archived plan/review evidence, and verification evidence. +- PASS without `Roadmap Targets`: `complete.log` omits `Roadmap Completion` and reported metadata says `roadmap-completion=none`. +- PASS split: empty active parent `agent-task/{task_group}/` removed after the subtask move; non-empty parent left in place. +- WARN/FAIL without user-review gate: new active `PLAN-{build_lane}-GNN.md` and `CODE_REVIEW-{review_lane}-GNN.md` created with matching headers and matching `구현 체크리스트`; no `complete.log`. +- Follow-up review stubs include the `사용자 리뷰 요청` section from `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` and forbid direct user prompts during implementation. +- USER_REVIEW: `USER_REVIEW.md` exists from template, no active `PLAN-*.md` or `CODE_REVIEW-*.md` remains, and no `complete.log` was written. +- Implementation-requested USER_REVIEW: archived review log preserves the filled `사용자 리뷰 요청` evidence and the generated `USER_REVIEW.md` records the exact user decision needed. +- USER_REVIEW resolved as PASS: archived task contains both resolved `USER_REVIEW.md` and `complete.log`. +- The applicable review-agent-only finalization checklist was completed before reporting. diff --git a/agent-ops/skills/common/code-review/agents/openai.yaml b/agent-ops/skills/common/code-review/agents/openai.yaml new file mode 100644 index 0000000..0ef4d88 --- /dev/null +++ b/agent-ops/skills/common/code-review/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Code Review" + short_description: "Review and route task loops" + default_prompt: "Use $code-review to review or resolve the active repository task, archive active files, and create complete.log, follow-up plan/review files, or USER_REVIEW.md." diff --git a/agent-ops/skills/common/code-review/templates/complete-log-template.md b/agent-ops/skills/common/code-review/templates/complete-log-template.md new file mode 100644 index 0000000..cadf8d7 --- /dev/null +++ b/agent-ops/skills/common/code-review/templates/complete-log-template.md @@ -0,0 +1,41 @@ +# Complete - {task_name} + +## 완료 일시 + +{YYYY-MM-DD or ISO-8601} + +## 요약 + +{one-line task summary, loop count, and final verdict; include user-review resolution if applicable} + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_{build_lane}_GNN_N.log` | `code_review_{review_lane}_GNN_N.log` | PASS/WARN/FAIL | {main outcome or follow-up reason} | +| `USER_REVIEW.md` | user decision | PASS/RESOLVED | {only when a user-review stop was resolved as the terminal completion path; remove this row otherwise} | + +## 구현/정리 내용 + +- {implemented or cleaned-up change} + +## 최종 검증 + +- `{command}` - {PASS/FAIL/BLOCKED}; {actual output summary or saved output path} + +## Roadmap Completion + +{optional; include only when archived plan/review had Roadmap Targets. Remove this entire section when there are no Roadmap Targets.} + +- Milestone: `{agent-roadmap/phase//milestones/.md}` +- Completed task ids: + - `{task-id}`: PASS; evidence=`{archived-plan-log}`, `{archived-review-log}`; verification=`{command or saved output path}` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-ops/skills/common/code-review/templates/user-review-template.md b/agent-ops/skills/common/code-review/templates/user-review-template.md new file mode 100644 index 0000000..70ff03b --- /dev/null +++ b/agent-ops/skills/common/code-review/templates/user-review-template.md @@ -0,0 +1,52 @@ +# User Review Required - {task_name} + +## 요청 일시 + +{YYYY-MM-DD or ISO-8601} + +## 상태 + +USER_REVIEW + +## 사유 + +- 유형: {loop-limit, environment-blocked, or implementation-blocked} +- 현재 리뷰 회차: {review-number} +- 최종 판정: {WARN or FAIL} +- 요약: {why automatic follow-up stopped} + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `{plan-log-0}` | `{code-review-log-0}` | {PASS/WARN/FAIL/unknown} | {main issue or blocking reason} | +| `{current-archived-plan-log}` | `{current-archived-review-log}` | {WARN/FAIL} | {main issue or blocking reason} | + +## 차단 근거 + +- 문제: {review finding summary} +- 현재 archive plan: `{current-archived-plan-log}` +- 현재 archive review: `{current-archived-review-log}` +- 검증 명령: `{command or 없음}` +- 실제 출력: {stdout/stderr excerpt or saved output path} +- 차단 판단 근거: {why this needs user decision or is outside repo-fixable scope, or 없음} + +## 사용자 결정 필요 + +- [ ] 자동 follow-up plan/review를 계속 진행한다. +- [ ] 계획을 재작성한다. +- [ ] 테스트 환경, secret, 외부 서비스, SDK, 장비 조건을 준비한 뒤 재시도한다. +- [ ] 작업 범위를 줄이거나 보류/폐기한다. + +## 재개 조건 + +- {what must be true before the next plan/review loop should start} + +## 다음 실행 힌트 + +- {suggested command, task path, or user decision needed to resume} + +## 종료 규칙 + +- 사용자가 이 stop state를 완료/PASS로 해소하면 `USER_REVIEW.md`를 해소 상태로 갱신하고, `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. +- 새 구현이 필요하면 `plan` 스킬이 `USER_REVIEW.md`를 `user_review_N.log`로 아카이브한 뒤 새 `PLAN-*-G??.md` / `CODE_REVIEW-*-G??.md`를 작성한다. diff --git a/agent-ops/skills/common/commit-push/SKILL.md b/agent-ops/skills/common/commit-push/SKILL.md new file mode 100644 index 0000000..c2fad97 --- /dev/null +++ b/agent-ops/skills/common/commit-push/SKILL.md @@ -0,0 +1,162 @@ +--- +name: commit-push +version: 1.2.0 +description: 변경 사항을 커밋하거나 원격에 푸시한다. "커밋해줘", "푸시해줘", "커밋하고 푸시", "올려줘" 요청 시 사용하며 명시된 푸시는 재확인 없이 수행한다. +--- + +# commit-push + +## 목적 + +현재 변경 내용을 분석하여 Conventional Commits 형식의 한국어 설명 커밋 메시지를 작성하고, 사용자가 요청한 커밋 또는 푸시를 수행한다. +일반적인 커밋/푸시는 승인 질문 없이 진행하고, 실제 위험 신호가 있을 때만 사용자 확인을 받는다. + +## 언제 호출할지 + +- "커밋해줘", "커밋하고 푸시해줘" 요청 시 +- "변경 사항 올려줘", "푸시해줘" 요청 시 +- 코드 작업 완료 후 "반영해줘", "올려줘" 요청 시 + +## 입력 + +- `scope`: 커밋 범위 — all(전체) 또는 특정 파일/경로 (선택, 기본값: all) +- `push`: 푸시 여부 (선택, 기본값: auto) + - auto: 요청에 "푸시", "push", "올려줘", "반영해줘", "커밋하고 푸시"가 있으면 true, "커밋만" 또는 "푸시하지 마"가 있으면 false + - true: 푸시 여부를 다시 묻지 않고 현재 브랜치를 원격에 푸시한다 + - false: 커밋만 수행하고 푸시는 생략한다 + +## 먼저 확인할 것 + +- [ ] 커밋할 변경 사항 또는 push할 unpushed commit이 존재하는가 (`git status`로 확인) +- [ ] 현재 브랜치, 원격, upstream 상태가 일반 커밋/푸시에 적합한가 +- [ ] `.env`, 시크릿 파일 등 민감한 파일이 스테이징에 포함되어 있지 않은가 + +## 실행 절차 + +1. **변경 사항 파악** + - `git status`로 변경/추가/삭제된 파일 목록을 확인한다 + - `git diff`로 구체적인 변경 내용을 확인한다 + - 커밋할 변경 사항이 없고 push가 true이며 unpushed commit이 있으면 커밋 단계는 건너뛰고 푸시를 진행한다 + - 커밋할 변경 사항도 없고 push할 unpushed commit도 없으면 사용자에게 알리고 종료한다 + +2. **민감 파일 검사** + - 스테이징 대상에 아래 패턴이 포함되면 해당 파일을 제외하고 사용자에게 경고한다 + - `.env`, `.env.*` + - `*secret*`, `*credential*`, `*password*` + - `*.pem`, `*.key`, `*.p12` + - `.gitignore`에 이미 등록된 파일은 무시한다 + +3. **커밋 메시지 작성** + - 변경 내용을 분석하여 Conventional Commits 형식으로 커밋 메시지를 작성한다 + - 아래 메시지 규칙을 따른다 + +4. **위험 신호 판단** + - 아래 위험 신호가 없으면 사용자 승인 없이 다음 단계로 진행한다 + - 위험 신호가 있으면 커밋/푸시를 멈추고 대상 파일, 커밋 메시지, 위험 내용을 짧게 보여준 뒤 사용자 확인을 받는다 + - 민감 파일 패턴이 스테이징 대상에 포함됨 + - 사용자가 지정한 scope 밖의 변경을 포함해야 함 + - 현재 브랜치가 사용자 요청이나 작업 맥락과 다를 가능성이 큼 + - merge/rebase 충돌, detached HEAD, git 작업 중 상태가 감지됨 + - 원격이 diverged 상태거나 일반 push가 실패해 force push가 필요해 보임 + - 사용자가 메시지 수정을 요청하면 반영한다 + +5. **커밋 실행** + - 커밋할 변경 사항이 없고 push할 unpushed commit만 있으면 이 단계를 건너뛴다 + - scope에 따라 파일을 스테이징한다 + - all: 변경된 전체 파일을 개별적으로 `git add` + - 특정 경로: 해당 파일만 `git add` + - 작성한 메시지로 `git commit`을 실행한다 + +6. **푸시 실행** + - push가 true인 경우에만 실행한다 + - 사용자가 푸시를 명시했으면 푸시 여부를 다시 묻지 않는다 + - 현재 브랜치를 원격에 푸시한다 + - 원격 브랜치가 없으면 `-u origin {branch}`로 설정한다 + - 일반 push가 실패하거나 충돌이 발생하면 사용자에게 알리고 중단한다 + - `--force`가 필요해 보여도 자동 실행하지 않고 사용자 확인을 받는다 + +7. **결과 보고** + - 커밋 해시, 메시지, 변경 파일 수, 푸시 결과를 출력한다 + +## 커밋 메시지 규칙 + +### 형식 + +``` +(): <한국어 서술형 요약> + +<왜 변경했는지 설명하는 한국어 본문> (선택) + +