Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,9 @@ def plan(self, raw_input: str) -> Plan:
- Group a task title, its explanatory lines, and its completion criteria into one task.
- Each genuinely independent deliverable becomes a separate task. Do not turn every detail or completion-criteria bullet into a separate task.
- By default, separate standalone action lines are separate tasks, especially short unindented lines like `cowork 고도화` and `지라 + 깃허브 연동`.
- Group lines only when their structure clearly marks them as context for a task, such as `작업 내용:`, `상세 내용:`, `완료 조건:`, or bullets under those sections.
- Each repeated `제목:` line starts a new task; attach the following `작업 내용:` and `완료 조건:` sections to that task until the next `제목:` line.
- Group lines only when their structure clearly marks them as context for a task, such as `작업 내용:`, `목적/배경:`, `상세 내용:`, `완료 조건:`, or bullets and checklist items under those sections.
- Each repeated `제목:` line starts a new task; attach the following `작업 내용:`, `목적/배경:`, `상세 내용:`, and `완료 조건:` sections to that task until the next `제목:` line.
- Treat `- [ ]` lines under `완료 조건:` as acceptance criteria, not as separate tasks, and preserve them under the `## 완료 조건` heading in description.
- Never merge separate standalone action lines merely because they belong to the same project or theme.
- A task may reference multiple related source lines. Split a single source line only when it clearly contains separate deliverables.
- Exclude completed items (- [x], strikethrough, 완료), schedules/meetings, and notes/thoughts.
Expand Down
108 changes: 93 additions & 15 deletions app/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,109 @@ <h1 id="login-title">할 일 올리기</h1>
<section class="input-guide" aria-labelledby="input-guide-title">
<div class="input-guide-heading">
<div>
<h1 id="input-guide-title">할 일 입력 가이드</h1>
<p>아래 가이드를 본인의 AI에게 전달하면 Cowork에 맞는 형식으로 정리할 수 있습니다.</p>
<h1 id="input-guide-title">AI와 대화해서 티켓을 구체화하세요</h1>
<p>가이드를 개인 AI에 전달하고 작업 아이디어를 입력한 뒤, 최종 결과만 Cowork에 붙여넣으세요.</p>
</div>
<button id="copy-guide-button" class="secondary copy-guide" type="button">가이드 복사</button>
</div>
<ul class="guide-rules">
<li>서로 독립적인 작업은 각각 별도 티켓으로 나뉩니다.</li>
<li>작업 내용과 완료 조건은 해당 티켓의 상세 내용으로 들어갑니다.</li>
<li>회의·일정·단순 메모·완료한 작업은 제외됩니다.</li>
<li>AI가 한 번에 한 가지씩 질문하며 부족한 내용을 구체화합니다.</li>
<li>독립적인 작업은 별도 티켓으로 나누고, 검증 가능한 완료 조건을 작성합니다.</li>
<li>담당자·역할·스프린트는 Cowork가 자동으로 적용합니다.</li>
<li>비밀번호·토큰·개인정보는 개인 AI에 입력하지 마세요.</li>
</ul>
<pre id="ai-guide-prompt" class="ai-guide-prompt">다음 내용을 Cowork에 붙여넣을 수 있는 작업 목록으로 정리해줘.
<pre id="ai-guide-prompt" class="ai-guide-prompt">당신은 애자일 프로젝트 관리와 Jira 티켓 작성을 돕는 인터뷰어입니다.

규칙:
1. 서로 독립적으로 실행할 작업은 각각 별도 항목으로 작성한다.
2. 각 작업은 아래 형식을 사용한다.
사용자가 입력한 메모나 작업 아이디어를 바로 티켓으로 추측해서 작성하지 말고, Jira 티켓으로 만들기에 부족한 정보를 대화를 통해 차근차근 확인하세요.

제목: 작업 제목
## 목표

사용자와 대화하여 서로 독립적으로 실행할 수 있는 작업을 구분하고, 각 작업에 대해 다음 정보를 확보합니다.

- 무엇을 해야 하는지
- 왜 이 작업이 필요한지
- 구체적으로 어떤 내용을 수행해야 하는지
- 완료 여부를 어떻게 검증할 수 있는지

## 인터뷰 진행 규칙

1. 사용자가 처음 입력한 내용을 먼저 분석합니다.
2. 서로 독립적으로 실행할 수 있는 작업이 여러 개라면 각각 별도 티켓 후보로 구분합니다.
3. 티켓 작성에 필요한 정보가 부족하면 한 번에 가장 중요한 질문 1개만 합니다.
4. 여러 티켓 후보 중 하나에 대해 질문할 때는 어떤 작업에 관한 질문인지 명확하게 표시합니다.
5. 사용자가 이미 제공한 정보를 반복해서 질문하지 않습니다.
6. 불필요하게 완벽한 정보를 요구하지 않습니다. 작업 내용과 검증 가능한 완료 조건이 충분히 명확하면 인터뷰를 종료합니다.
7. 사용자가 모르는 정보는 억지로 답하게 하지 말고, 최종 티켓 작성에 꼭 필요한 정보인지 판단합니다.
8. 사용자가 “최종 정리”, “티켓으로 정리”, “이대로 작성”과 같이 요청하면 정보가 충분한 범위에서 최종 결과를 출력합니다.

## Cowork에서 자동으로 처리하는 정보

다음 정보는 Cowork가 로그인 정보와 화면 선택값을 기준으로 자동 적용하므로 질문하거나 최종 결과에 포함하지 않습니다.

- Jira 담당자
- 역할 태그와 제목 접두어
- Jira Label
- 프로젝트와 티켓 유형
- 스프린트

우선순위는 현재 Cowork가 반영하지 않으므로 질문하거나 최종 결과에 포함하지 않습니다.

## 티켓 분리 규칙

1. 서로 독립적으로 구현, 검증 또는 배포할 수 있는 작업은 각각 별도 티켓으로 분리합니다.
2. 하나의 작업을 수행하기 위한 세부 단계는 별도 티켓으로 과도하게 나누지 않습니다.
3. 회의, 일정, 단순 메모, 아이디어만 있는 내용, 이미 완료된 작업은 티켓에서 제외합니다.
4. 제외 여부가 불명확하면 추측하지 말고 사용자에게 질문합니다.

## 사실 기반 규칙

1. 사용자가 말하지 않은 원인, 기술 스택, 구현 방식, 일정 또는 완료 조건을 임의로 만들지 않습니다.
2. 불명확한 내용은 추측하지 말고 질문합니다.
3. 사용자가 특정 구현 방식을 정하지 않았다면 임의로 결정하지 않습니다.
4. 완료 조건은 실제로 확인할 수 있는 문장으로 작성합니다.
5. “정상적으로 동작한다”, “문제가 없다”처럼 검증 방법이 불명확한 표현은 가능한 한 구체화합니다.

## 최종 출력 조건

각 티켓에 대해 다음 정보가 확보되면 최종 결과를 출력합니다.

- 수행할 작업이 명확함
- 목적 또는 배경이 확인됨
- 구체적인 작업 범위가 확인됨
- 검증 가능한 완료 조건이 1개 이상 있음

정보가 부족하면 최종 형식을 억지로 채우지 말고 인터뷰를 계속합니다.

## 최종 출력 형식

각 티켓은 반드시 아래 형식을 정확히 사용합니다.

제목: 수행할 행동이 드러나는 간결한 제목
작업 내용:
- 수행할 내용
- 목적/배경: 작업이 필요한 이유
- 상세 내용: 수행할 구체적인 작업 내용
완료 조건:
- 확인 가능한 완료 기준
- [ ] 검증 가능한 완료 기준 1
- [ ] 검증 가능한 완료 기준 2

여러 티켓이 필요한 경우 위 형식을 반복합니다.

## 제목 작성 규칙

- 수행할 행동이 명확하게 드러나게 작성합니다.
- 예: 로그인 오류 수정, 배포 실패 알림 추가, 사용자 검색 성능 개선
- FE, BE, Infra, AI 등의 역할 접두어는 붙이지 않습니다.
- 담당자 이름, 스프린트 이름, 프로젝트 키를 넣지 않습니다.

## 최종 출력 방식

1. 최종 결과에는 인터뷰 내용이나 설명을 덧붙이지 않습니다.
2. “다음과 같이 정리했습니다” 같은 인사말을 쓰지 않습니다.
3. 마크다운 코드 블록으로 감싸지 않습니다.
4. Cowork 입력창에 그대로 붙여넣을 수 있는 티켓 본문만 출력합니다.
5. 사용자가 제공하지 않은 내용을 채워 넣지 않습니다.

3. 회의, 일정, 단순 메모, 이미 완료한 작업은 제외한다.
4. 입력에 없는 사실이나 완료 조건을 새로 만들지 않는다.
5. 마크다운 코드 블록이나 추가 설명 없이 정리된 작업 목록만 출력한다.</pre>
인터뷰 중에는 자연스러운 대화형 답변을 사용할 수 있지만, 최종 출력에서는 위 형식의 티켓 본문만 출력하세요.</pre>
<p id="copy-guide-status" class="copy-guide-status" aria-live="polite"></p>
</section>
<div class="composer">
Expand Down
28 changes: 27 additions & 1 deletion tests/test_ui_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ def test_screen_contract_and_forbidden_vocabulary():
html = Path("app/static/index.html").read_text(encoding="utf-8")
script = Path("app/static/app.js").read_text(encoding="utf-8")
backend = Path("app/main.py").read_text(encoding="utf-8")
planner = Path("app/agent.py").read_text(encoding="utf-8")
visible_contract = html + script
full_contract = visible_contract + backend

for required in (
"할 일 올리기",
"적어두면 Jira 티켓으로 만들어드려요",
"name@company.com",
"할 일 입력 가이드",
"AI와 대화해서 티켓을 구체화하세요",
"가이드 복사",
"한 번에 가장 중요한 질문 1개만 합니다",
"담당자·역할·스프린트는 Cowork가 자동으로 적용합니다",
"목적/배경: 작업이 필요한 이유",
"상세 내용: 수행할 구체적인 작업 내용",
"검증 가능한 완료 기준 1",
"최종 결과만 Cowork에 붙여넣으세요",
"작업 목록을 입력하거나 AI가 정리한 내용을 붙여넣어 주세요",
"AI에게 전달할 가이드를 복사했습니다",
"티켓 만들기",
Expand Down Expand Up @@ -56,6 +63,8 @@ def test_screen_contract_and_forbidden_vocabulary():
assert "setProgress" in script
assert "progress.classList.toggle('loading'" in script
assert "navigator.clipboard.writeText" in script
assert "Treat `- [ ]` lines under `완료 조건:` as acceptance criteria" in planner
assert "`목적/배경:`" in planner
assert "오늘 할 일을 편하게 적어주세요" not in html

for forbidden in ("이슈", "이슈 타입", "백로그", "에픽", "스토리", "하위 작업", "어사인", "담당자 배정"):
Expand All @@ -70,6 +79,23 @@ def test_receipts_are_not_persisted_in_browser_storage():
assert "cowork_pending_request" in script


def test_copied_interview_guide_keeps_parser_contract():
html = Path("app/static/index.html").read_text(encoding="utf-8")
prompt = html.split('<pre id="ai-guide-prompt" class="ai-guide-prompt">', 1)[1].split(
"</pre>", 1
)[0]

labels = ["제목:", "작업 내용:", "완료 조건:"]
positions = [prompt.index(label) for label in labels]
assert positions == sorted(positions)
assert all(prompt.count(label) == 1 for label in labels)
assert "- 목적/배경:" in prompt
assert "- 상세 내용:" in prompt
assert "- [ ] 검증 가능한 완료 기준 1" in prompt
assert "우선순위는 현재 Cowork가 반영하지 않으므로" in prompt
assert "질문하거나 최종 결과에 포함하지 않습니다" in prompt


def test_static_asset_urls_include_current_content_hash():
html = Path("app/static/index.html").read_text(encoding="utf-8")
for asset in ("app.js", "style.css"):
Expand Down