pharos로 Azure DevOps 작업하기
pharos는 셸에서 Azure DevOps를 사용할 수 있게 해주는 CLI입니다. 브라우저 로그인 대신 환경 변수로 인증합니다.
할 수 있는 모든 작업은 아래에 나열되어 있습니다. pharos --help를 실행하여 알아내지 마세요 — 해당 명령은 여러 번의 호출이 필요하며, 이 섹션이 동일한 정보를 제공합니다.
whoami ADO_PAT가 속한 사용자. @Me는 이것으로 확인됩니다.
types --state와 --type이 어떤 값을 ACCEPT하는지, 유형별로
query 어떤 작업 항목 — 나에게 할당되었거나, 스프린트에 있거나,
특정 유형이거나, 아직 열려 있는 항목. ID가 아닌, 수화된 항목입니다.
task <id> 하나의 작업 항목 전체: 필드, 댓글,
첨부 파일, 제목이 포함된 관계, 그리고 연결된 모든 위키 페이지의
콘텐츠 + 토론
--children EPIC과 그 하위 항목을 한 번의 호출로 가져옵니다.
--depth <n>으로 더 깊게 (최대 5)
--compact ID를 평탄화하고 보드 키를 제거합니다.
create <type> --title "…" 하나의 작업 항목을 생성합니다. --parent는 동일한 패치에 포함됩니다.
update <id> 필드 변경: --state --priority --assignee
--title, 또는 다른 항목은 --field Name=value 사용
link <id> --parent <id> 두 항목을 연결합니다. --child --related 도 가능합니다.
unlink <id> --parent <id> --predecessor --successor --duplicate
link <id> --wiki-page <path> 위키 페이지를 작업 항목에 연결합니다 (작업 항목 쪽).
wiki links <path> 어떤 작업 항목이 페이지를 연결하는지 (ADO는 API가 없음).
wiki link <path> --item <id> 위키 쪽에서 연결합니다. --item은 반복 가능합니다.
wiki unlink <path> --item <id>
history <id> 무엇이 CHANGED되었는지, 필드별로, 누가 언제 했는지
iterations | areas 날짜가 포함된 스프린트와 영역
links 이 ORG가 가진 모든 관계 유형과, 어떤
--flag가 그에 도달하는지 (약 18개 중 6개)
fields [--type T] 필드가 ACCEPT할 값 — 허용된 값
attach <id> <file> 작업 항목에 FILE 추가 (첨부 파일 목록)
detach <id> <url-or-guid> 첨부 파일을 제거합니다. --yes 필요. 또는 --name <file>
download <item-id|guid|url> 첨부 파일을 읽어옵니다. WORK ITEM id와 --name <file> 함께 사용,
또는 --all --out <dir>. --out이 없으면 기록되지 않습니다.
image <file> 텍스트 내에서 사용할 PICTURE를 업로드합니다. 붙여넣을 markdown을
출력합니다. attach와는 다릅니다.
people [query] @mentioned할 수 있는 사람, @<guid> 형식으로
delete <id> --yes → 휴지통 (영구 삭제는 의도적으로 없음).
restore <id> 복원합니다.
deleted 휴지통에 있는 항목과 이름을 보여줍니다.
--top <n> — 기본적으로 50개를 반환하며 실제 총 개수를
"count"로 보고합니다.
wiki list | tree | read <path> | write <path> | delete <path>
wiki move <path> <new path> 페이지 이동; 하위 페이지도 함께 이동합니다.
wiki rename <path> <new name> 동일한 호출, 리프만 해당. 둘 다 --yes 필요.
wiki duplicate <path> [to] Azure DevOps에 없는 동사. "<path> - 복사 N"
wiki image <file> PAGE 또는 페이지 댓글용 그림입니다. `image`와는
다른 엔드포인트이며, 이름은 고유하게 지정됩니다.
wiki import <file...> .md .txt .docx .pdf .rtf .html -> 페이지. .pptx는
의도적으로 거부됩니다 — 아래 참조.
--under <path> --as <name>
comment list | add | edit | delete <target>은 작업 항목 ID 또는 위키 경로입니다.
comment react | unreact | reactors 좋아요 싫어요 하트 축하 웃음 혼란
hooks list | check | create | repoint | delete 실시간 서비스 후크.
`check`는 --hub <url> 필요; `list`는 이미 사용 중인 URL 표시.
plan <file> 구현 계획 → 작업 항목 트리
setup 조직, 프로젝트, 토큰 → 키체인 + 셸 프로필
텍스트 입력: --text / --file / --stdin. 전역 옵션: 사람을 위한 --pretty,
파괴적 동사를 위한 --yes, 미리보기를 위한 --dry-run.
ADO_ORG, ADO_PROJECT 및 ADO_PAT 은 설정 후 환경에 이미 있습니다. 작업 전에 확인하지 마세요. 누락되면 종료 코드 2와 함께 "kind": "config"로 알려주며, 이때만 신경 쓰면 됩니다.
작업 찾기: pharos query
curl과 WIQL 엔드포인트를 사용하지 마세요. 이 스킬은 이전에 query 동사가 없었기 때문에 정확히 그 레시피를 제공했습니다. 지금은 query 동사가 있으며, 레시피가 할 수 없었던 부분, 즉 WIQL은 ID만 반환하므로 레시피는 16개의 숫자와 각 항목을 의미 있게 만들기 위한 항목별 호출을 제공했습니다.
pharos query --mine # 나에게 할당되었고, 아직 열려 있는 항목
pharos query --sprint "Sprint 1" # --sprint current는 @currentIteration 용
pharos query --type Epic --state Doing # 둘 다 반복 가능
pharos query --assignee "[email protected]"
pharos query --tag api --all # --all은 완료된 작업 포함
pharos query --wiql "SELECT [System.Id] FROM WorkItems WHERE …" # 탈출구
플래그는 AND로 결합됩니다. 출력은 수화된 항목(id, 유형, 제목, 상태, 담당자, 반복, 태그, 우선순위, 변경일)이므로 --mine은 쿼리 후 결과별로 가져오는 것이 아니라 하나의 명령입니다. 실제로 작업할 소수에 대해서만 pharos task <id>로 후속 조치하세요.
숫자를 보고하기 전에 openness 필드를 읽으세요. "12개 열림"은 무엇이 완료된 것으로 계산되었는지 알기 전까지는 의미가 없으며, Azure DevOps는 프로세스 템플릿이 모든 상태를 이름을 바꿀 수 있습니다. query는 프로젝트 자체의 상태 범주를 읽고 어떤 상태를 종결 상태로 처리했는지 알려줍니다. 읽을 수 없으면 그렇게 말하고 추측으로 대체하며, 이때는 명시적으로 --state를 전달해야 합니다. assignedTo는 @Me가 실제로 확인된 사람을 나타냅니다. 공유 또는 서비스 토큰은 "나에게 할당됨"이 조용히 다른 사람을 의미할 수 있습니다.
--all은 다른 모든 필터와 결합되므로, --mine --all은 나에게 할당된 항목이 없음과 열린 항목이 없음을 구분하는 방법입니다. 단독 pharos query --all은 유효하며 전체 프로젝트를 반환합니다.
--sprint은 이름 또는 fieldPath를 허용하고 두 값을 정규화합니다. fieldPath 규칙은 System.IterationPath를 필드 값으로 사용하는 것입니다. 그러나 명명된 스프린트는 UNDER(하위 반복 포함)로 일치하고 --sprint current는 =(정확히 일치)로 일치하므로 둘은 동일한 쿼리가 아닙니다.
--wiql은 FROM WorkItemLinks … MODE (MustContain) 쿼리도 허용하며 동일한 방식으로 ID를 수화하므로, 한 번의 호출로 주어진 관계 유형을 가진 모든 항목을 찾을 수 있습니다.
--wiql을 사용한다면 [System.State] NOT IN GROUP 'Completed'를 작성하지 마세요. 구문 분석되고 200을 반환하며 모든 항목과 일치합니다. IN GROUP은 작업 항목 TYPE 범주만 다루며, 알 수 없는 그룹은 오류 없이 빈 집합으로 확인됩니다. 2026-08-05 측정.
작업 항목 변경
pharos update 225 --state Doing # 작업이 이동할 때 상태 이동
pharos update 225 --priority 1 --assignee "[email protected]"
pharos update 225 --field Microsoft.VSTS.Scheduling.RemainingWork=3
pharos attach 225 ./bestsellers.xlsx --comment "수치입니다"
update는 항목을 읽고 /rev에 대한 test 연산 하에 변경 사항을 적용하므로, 읽기와 쓰기 사이에 다른 사람이 작성한 경우 편집 내용을 잃지 않고 conflict가 발생합니다. 이미 있는 값을 설정하면 아무것도 쓰지 않고 알려줍니다. 의미 없는 PATCH는 여전히 System.Rev를 증가시키고 항목의 다른 모든 캐시된 리비전을 무효화합니다. --dry-run은 변경 전 → 후를 보여주고 아무것도 쓰지 않습니다. --yes는 없습니다. 필드 편집은 일반 편집이며 Azure DevOps는 모든 리비전을 유지합니다.
attach는 바이트를 업로드한 다음 AttachedFile 관계로 연결합니다. 두 번의 호출, 하나의 명령입니다. 첨부 파일은 변경 불가능합니다. 동일한 파일을 두 번 첨부하면 두 개가 생성되며, 대체나 버전 관리가 없습니다. detach는 하나를 제거합니다. --yes가 필요하며, 위치가 아닌 ID로 첨부 파일을 찾고 바이트는 Azure DevOps에 남겨두므로 URL을 다시 첨부하면 복원됩니다. --name <file>로 파일 이름을 지정하거나 GUID를 전달합니다. download와 마찬가지로, 이름이 같은 두 파일은 먼저 정렬되는 것을 제거하지 않고 거부합니다.
create는 필드와 동일한 패치에 --parent를 넣으므로 자식이 잠시 고아가 되지 않으며, update와 동일한 플래그 이름을 사용합니다.
unlink는 추측하지 않고 거부합니다. 이를 미리 알아두는 것이 좋습니다. Azure DevOps는 배열의 POSITION으로 관계를 제거하므로, 오래된 읽기의 인덱스는 다른 링크를 끊고 요청은 여전히 성공합니다. unlink는 ID로 관계를 찾고 읽은 리비전에 대한 test 연산 하에 제거합니다. 관계가 없으면 종료 코드 3을 반환합니다. 이는 아무 일도 일어나지 않았음을 의미하며, 호출이 실패한 것은 아닙니다.
delete는 휴지통으로 이동하며 --yes가 필요합니다. 거부 시 먼저 제목을 인용합니다. restore <id>는 되돌리며 플래그가 필요하지 않습니다.
의도적으로 영구 삭제는 없습니다. Azure DevOps에서 유일하게 되돌릴 수 없는 동사이며, --yes는 전달하는 법을 배운 플래그입니다. 웹 UI가 이를 담당합니다. 누군가 정말로 제거해야 한다면 플래그를 찾지 말고 웹 UI로 보내세요.
텍스트 속 그림과 파일
이 둘은 서로 다른 것이며, 잘못 선택하는 것이 피해야 할 실수입니다.
pharos attach 225 ./bestsellers.xlsx # FILE, 첨부 파일 목록에 표시됨
pharos image ./screenshot.png # PICTURE, 텍스트 안에 넣기 위함
첨부 파일은 AttachedFile 관계를 생성하고 작업 항목의 첨부 파일 목록에 나타납니다. 인라인 이미지는 관계를 전혀 생성하지 않습니다. #333에서 측정된 바에 따르면, 설명에 붙여넣은 두 개의 스크린샷은 relations: 0과 attachments: []를 제공했습니다. 이는 필드 텍스트의 일 뿐이며, 이것이 첨부 파일 목록에 아무것도 표시되지 않는 이유입니다.
따라서 image는 markdown 줄을 출력하고 쓰기는 사용자에게 맡깁니다:
pharos image ./chart.png --pretty
# 
pharos update 225 --field System.Description="$(cat <<'EOF'
수익이 증가했습니다. 차트를 참조하세요:

EOF
)"
위키 그림은 다른 엔드포인트이며 wiki image가 필요합니다. 위키는 git 저장소이므로, 거기의 첨부 파일은 FILE이며 NAME이 ID입니다. 두 번째 image.png를 업로드하면 첫 번째 것 위에 저장되며, /.attachments/image.png를 가리키는 모든 페이지는 조용히 그림이 변경됩니다. wiki image는 보내기 전에 이름을 고유하게 만들고 저장소-상대 링크를 출력합니다:
pharos wiki image ./diagram.png --pretty
# 
절대 URL이 아닌 해당 경로를 사용하세요. wit/attachments URL에 링크하는 페이지는 세션이 있는 사람에게는 렌더링되고 다른 사람에게는 깨집니다. 동일한 markdown이 페이지와 페이지 댓글에서 작동합니다.
작업 항목에서 첨부 파일 가져오기
pharos task <id>는 각 파일의 GUID와 함께 첨부된 내용을 나열합니다. JSON 및 --pretty에서 확인할 수 있습니다. 그런 다음 이름으로 한 번에 가져옵니다:
pharos download 41 --name Skills.pptx --out ./Skills.pptx
pharos download 41 --all --out ./attachments # 항목의 모든 첨부 파일
pharos download <guid-or-url> --out ./file.bin # 이미 GUID/URL이 있을 때
--out이 실제로 쓰는 옵션입니다. 없으면 크기만 반환하고 파일은 생성되지 않습니다. 원시 바이트를 stdout으로 보내면 다른 모든 동사가 출력하는 JSON이 손상됩니다.
단독 pharos download 41은 이름, GUID 및 크기 목록을 포함하는 사용 오류입니다. 따라서 올바른 파일을 선택하는 데 두 번째 호출이 필요하지 않습니다. 동일한 이름의 두 첨부 파일은 추측하지 않고 거부합니다. 첨부 파일은 변경 불가능하므로 파일을 두 번 첨부하면 두 개가 생성되며 이름은 ID가 아닙니다.
detach는 동일한 --name <file>을 사용하므로, task --pretty를 읽은 후 사용하는 동사가 표시한 내용을 그대로 받아들입니다:
pharos detach 41 --name Skills.pptx --yes # --dry-run이 먼저 미리보기
내부 내용 읽기는 사용자의 도구로 YOUR 도구로 처리합니다.
download는 바이트를 디스크에 저장하고 거기서 멈춥니다. .docx, .pptx, .xlsx 또는 .pdf를 읽을 수 있는 것으로 변환하는 것은 사용자 환경의 작업입니다.
이를 위해 pharos-convert를 사용하지 마세요. 이것은 wiki import의 변환기로, macOS 앱과 공유되어 동일한 파일에서 동일한 페이지를 생성합니다. 그 출력은 위키 페이지가 되도록 형성되며, 여기서 이것을 사용하면 읽는 내용이 앱의 가져오기 경로에 결합됩니다. 또한 .pptx를 전혀 읽을 수 없습니다.
두 가지 방식으로 조용히 실패하며, 둘 다 실제 작업 항목에서 측정되었습니다:
- 도구를 잘못된 위치에서 확인했습니다. 시스템 PATH에 대한
which markitdown과 시스템python3에 대한import docx는 둘 다 해당 도구가 스킬별 venv에 설치된 머신에서 비어 있습니다. 이 두 검사를 실행하는 에이전트는 "아무것도 없다"고 결론내리고 실제로 있었던 도구를 우회합니다. 도구가 없다고 결론내리기 전에 사용자의 에이전트가 도구를 보관하는 위치를 확인하세요. - 비어 있지 않은 텍스트가 문서를 읽었다는 증거는 아닙니다. 실제
.pptx하나는 약 90자, 7개의 조각으로 추출되었습니다. 전체 사양은 두 개의 포함된 PNG에 있었습니다. 모든 텍스트 전용 리더는 해당 덱에 대해 무언가를 반환하고 작동한 것처럼 보입니다. 슬라이드 덱 또는 스캔된 PDF의 경우 페이로드는 일반적으로 이미지입니다. 이를 추출하고 실제로 살펴보세요.
먼저 PDF를 기본적으로 읽을 수 있는지 물어보세요. 가능하다면, 이것은 하나의 명령입니다.
많은 에이전트(Claude Code 포함)는 PDF를 시각적으로, 페이지별로, 사람이 보는 방식으로 읽습니다. 이는 텍스트가 전혀 없는 스캔도 OCR 단계 없이 다룹니다. 해당되는 경우 전체 문제는 하나의 변환으로 축소됩니다:
soffice --headless --convert-to pdf f.docx --outdir ./out # .pptx, .xlsx도 가능

