This document is read via Read from the command (
commands/nopal.md) as reference material. It is the core logic that receives user requests and dynamically combines and executes Google Workspace services.
Supported Services (9)
| Service | Helper Command | Primary Use |
|---|---|---|
| Gmail | +send, +triage, +watch |
Send/read/manage email |
| Calendar | +insert, +agenda |
Manage schedules/events |
| Drive | +upload |
Manage files/folders/sharing |
| Sheets | +read, +append |
Read/write spreadsheets |
| Docs | +write |
Read/write documents |
| Slides | (direct API) | Create/edit presentations |
| Chat | +send |
Chat spaces/messages |
| Tasks | (direct API) | Manage to-do lists/tasks |
| Meet | (direct API) | Create meeting links/participants/recording/transcripts |
Workflow (5 Steps)
Step 1: Identify Intent
Analyze the user's natural language request to determine the following:
1-1. Identify Required Services
Extract keywords and intent from the request to determine which of the 9 services are needed.
| Keyword/Intent | Mapped Service |
|---|---|
| mail, email, send, receive, reply | Gmail |
| schedule, meeting, meetup, appointment, calendar, schedule | Calendar |
| file, folder, upload, download, share, drive | Drive |
| spreadsheet, Excel, table, data, sheet, total, sales | Sheets |
| document, writing, create, edit, report, meeting minutes | Docs |
| presentation, slides, presentation, PPT | Slides |
| chat, message, notification | Chat |
| to-do, task, checklist, to-do | Tasks |
| video conference, Meet, meeting link, recording, participants, transcript | Meet |
Multiple services can be involved in a single request. Example: "Send an email to the meeting attendees" → Calendar + Gmail
1-2. Determine Execution Type
| Type | Description | Example |
|---|---|---|
| Simple lookup | Read information only | "Tell me today's schedule" |
| Simple execution | One service action | "Send an email" |
| Composite combination | Chaining multiple services | "Write meeting minutes and share them with attendees" |
Step 2: Interview (Collect Missing Information)
Principle: Minimize questions. Ask only what is necessary.
2-1. Information Gap Analysis (Internal Processing)
List the parameters needed to execute the request, and distinguish between those already provided and those missing.
Example — "Schedule a team meeting tomorrow at 2 PM":
- Date: tomorrow (given)
- Time: 2 PM (given)
- Title: team meeting (given)
- Attendees: ? (missing)
- Duration: ? (missing, can use default of 1 hour)
- Location/link: ? (missing)
2-2. Query existing data, then present options
If possible, query data from Google Workspace first and reflect it in the AskUserQuestion options.
Example — instead of "Which meeting would you like to prepare?":
gws calendar +agenda --days 3 --format jsonexecution- Extract meeting list from the result
- Present the meeting list as options via AskUserQuestion
AskUserQuestion({
"questions": [
{
"question": "어떤 회의를 준비할까요?",
"header": "회의 선택",
"multiSelect": false,
"options": [
{"label": "내일 10:00 — 주간 스탠드업", "description": "참석자 5명, Google Meet"},
{"label": "내일 14:00 — 프로젝트 리뷰", "description": "참석자 3명, 회의실 B"},
{"label": "모레 09:00 — 전체 회의", "description": "참석자 12명, 대회의실"}
]
}
]
})
2-3. Call AskUserQuestion
Call the AskUserQuestion tool only when there are missing parameters.
Rules:
- Do not ask questions in text. Always use the AskUserQuestion tool.
- Ask questions bundled together at once (minimize round trips).
- Do not ask about parameters that can be resolved with a default value; use the default instead.
- Meeting duration: default 1 hour
- Email format: default plain text
- File sharing permission: default "viewable"
- Ask a clarifying question only for ambiguous requests.
Interview pattern example:
요청: "회의 준비해줘"
→ [Calendar 조회 후] "어떤 회의를 준비할까요?" [조회 결과를 옵션으로 제시]
→ "자료도 공유할까요?" [예 / 아니오]
→ "참석자에게 메일도 보낼까요?" [예 / 아니오]
요청: "보고서 보내줘"
→ [Drive 조회 후] "어떤 파일을 보낼까요?" [최근 파일 목록으로 제시]
→ "누구에게 보낼까요?" [텍스트 입력]
요청: "이번 주 할일 정리해줘"
→ [Tasks 조회 후] 바로 정리 (추가 질문 불필요)
Step 3: Create a plan
3-1. Loading references
Read only the reference files for the required services selectively.
Read ${CLAUDE_PLUGIN_ROOT}/references/gws-shared.md (항상 읽기)
Read ${CLAUDE_PLUGIN_ROOT}/references/gws-{서비스명}.md (필요한 서비스만)
Example: for a Gmail + Calendar combination:
Read ${CLAUDE_PLUGIN_ROOT}/references/gws-shared.md
Read ${CLAUDE_PLUGIN_ROOT}/references/gws-gmail.md
Read ${CLAUDE_PLUGIN_ROOT}/references/gws-calendar.md
The references/ file may not exist yet. If it does not, operate based on the command guide and helper list in this SKILL.md.
3-2. Check preset patterns
In the references/ folder, workflows.md, recipes.mdif there is one, refer to it and use verified patterns first.
Even if there is none, you can dynamically generate a new combination. Do not refuse just because it is not in a preset.
3-3. Dynamically generate ExecutionPlan
List the gws commands to execute in order. Each step includes the following information:
| Field | Description |
|---|---|
| order | Execution order (1, 2, 3, ...) |
| service | Service used (Gmail, Calendar, ...) |
| command | Actual gws command |
| depends_on | If the result of a previous step is needed, the corresponding order number |
| description | What this step does (Korean, easy for humans to read) |
Example ExecutionPlan:
| # | 서비스 | 작업 | 의존 |
|---|--------|------|------|
| 1 | Calendar | 내일 일정 조회 | - |
| 2 | Docs | 회의록 문서 생성 | - |
| 3 | Drive | 회의록 참석자에게 공유 | 1, 2 |
| 4 | Gmail | 참석자에게 회의록 링크 메일 발송 | 1, 2, 3 |
3-4. User confirmation (conditional)
For simple read-only queries, execute immediately without confirmation.
| Execution type | Confirmation | Reason |
|---|---|---|
| Simple query (read-only) | Skip → go straight to Step 4 | No side effects. Speed first |
| Simple execution (1 write) | Confirmation required | Data change |
| Composite combination (2+ services) | Confirmation required | Multiple service changes |
Simple query determination criteria:
- Use only one service
- The command is
list,get,+triage,+agendaetc. read-only create,send,insert,update,delete,trash,modify,patchetc. no write operations
If it is not a simple query, show the plan as a Markdown table and get confirmation.
EXECUTE: Call the AskUserQuestion tool to get confirmation:
AskUserQuestion({
"questions": [
{
"question": "이렇게 실행할까요?",
"header": "실행 계획",
"multiSelect": false,
"options": [
{
"label": "네, 실행해주세요 (추천)",
"description": "위 계획대로 순서대로 실행합니다."
},
{
"label": "수정할 부분이 있어요",
"description": "변경하고 싶은 단계를 알려주세요."
},
{
"label": "취소",
"description": "실행하지 않고 종료합니다."
}
]
}
]
})
- "Execute" → proceed to Step 4
- "Modify" → reflect user feedback and re-establish the plan
- "Cancel" → exit
Step 4: Execute
Execute the ExecutionPlan confirmed by the user in order via Bash.
4-1. Command execution rules
| Rule | Description |
|---|---|
| Helper first | Helper commands (+send, +agenda etc.) for services that have helpers should be used first |
| JSON output | For all commands, --format json use the flag |
| Result parsing | After each step executes, extract the ID/value needed for the next step from the JSON result |
| Error isolation | Even if one step fails, continue with the remaining steps |
4-2. Command guide by service
Gmail
Email sending — required rule for Korean encoding:
gws gmail +senddoes not RFC 2047-encode Korean subject lines, so they are corrupted on receipt.
Emails containing Korean must use the raw API method below.
# 이메일 보내기 (한글 지원 — raw API)
# 1. RFC 2822 메시지를 만들어 base64url로 인코딩
RAW=$(node -e "
const to='user@example.com';
const subject='한글 제목';
const body='한글 본문 내용';
const mime=[
'MIME-Version: 1.0',
'Content-Type: text/plain; charset=utf-8',
'Content-Transfer-Encoding: base64',
'To: '+to,
'Subject: =?UTF-8?B?'+Buffer.from(subject).toString('base64')+'?=',
'',
Buffer.from(body).toString('base64')
].join('\r\n');
process.stdout.write(Buffer.from(mime).toString('base64url'));
")
gws gmail users messages send --params '{"userId":"me"}' --json "{\"raw\":\"$RAW\"}"
# CC/BCC 포함 시 mime 배열에 추가:
# 'Cc: cc@example.com',
# 'Bcc: bcc@example.com',
# 이메일 목록 조회 (헬퍼 — 추천)
# "안 읽은 메일" → 기본 쿼리(is:unread) 사용
gws gmail +triage --format json
# "최근 메일", "지금 온 메일" → 읽음 상관없이 최근 메일 전체
gws gmail +triage --max 10 --query 'newer_than:1d' --format json
# 특정 조건 검색
gws gmail +triage --max 10 --query 'from:boss' --format json
# 주의: "메일 확인해줘", "지금 온 메일" 등 일반적 요청은 is:unread가 아닌
# newer_than:1d 또는 조건 없이 최근 메일을 보여준다. "안 읽은 메일"이라고
# 명시한 경우에만 is:unread를 사용한다.
# 이메일 목록 조회 (API — 세밀한 검색 시)
# 주의: 반드시 "users messages"로 호출. "messages"만 쓰면 에러남
gws gmail users messages list --params '{"userId":"me","maxResults":10}' --format json
# 이메일 상세 읽기
gws gmail users messages get --params '{"userId":"me","id":"MESSAGE_ID"}' --format json
# 이메일 휴지통 이동 (gws 0.6.1+ 에서 411 버그 수정됨)
gws gmail users messages trash --params '{"userId":"me","id":"MESSAGE_ID"}'
Calendar
# 일정 조회 (헬퍼)
gws calendar +agenda --days 7 --format json
# 일정 생성 (헬퍼)
# --summary (NOT --title), --start, --end 필수. --location, --description, --attendee 선택
gws calendar +insert --summary "회의 제목" --start "2026-03-06T14:00:00+09:00" --end "2026-03-06T15:00:00+09:00"
gws calendar +insert --summary "팀 미팅" --start "2026-03-06T14:00:00+09:00" --end "2026-03-06T15:00:00+09:00" --attendee "alice@example.com" --location "회의실 B"
# 일정 생성 (참석자 포함, 직접 API)
gws calendar events insert --params '{"calendarId": "primary"}' --json '{
"summary": "팀 회의",
"start": {"dateTime": "2026-03-06T14:00:00+09:00"},
"end": {"dateTime": "2026-03-06T15:00:00+09:00"},
"attendees": [
{"email": "alice@example.com"},
{"email": "bob@example.com"}
]
}' --format json
# 일정 수정
gws calendar events patch --params '{"calendarId": "primary", "eventId": "EVENT_ID"}' --json '{
"summary": "수정된 제목"
}' --format json
# 일정 삭제
gws calendar events delete --params '{"calendarId": "primary", "eventId": "EVENT_ID"}'
Drive
# 파일 업로드 (헬퍼) — 파일 경로는 위치 인자 (--file 아님)
gws drive +upload /path/to/file.pdf
gws drive +upload /path/to/file.pdf --parent FOLDER_ID --name "새이름.pdf"
# 파일 목록 조회
gws drive files list --params '{"q": "name contains '\''Report'\''", "pageSize": 10}' --format json
# 파일 공유 (권한 추가)
gws drive permissions create --params '{"fileId": "FILE_ID"}' --json '{
"role": "reader",
"type": "user",
"emailAddress": "user@example.com"
}' --format json
# 폴더 생성
gws drive files create --json '{
"name": "새 폴더",
"mimeType": "application/vnd.google-apps.folder"
}' --format json
Sheets
# 시트 읽기 (헬퍼) — --spreadsheet (NOT --spreadsheet-id)
gws sheets +read --spreadsheet "SHEET_ID" --range "Sheet1!A1:D" --format json
# 한글 시트명은 range에서 제외하고 A1:D만 쓰면 첫 시트 자동 선택
gws sheets +read --spreadsheet "SHEET_ID" --range "A1:D10" --format json
# 시트에 데이터 추가 (헬퍼) — --spreadsheet, --values (단일 행) 또는 --json-values (다중 행)
gws sheets +append --spreadsheet "SHEET_ID" --values '이름,점수,홍길동,95'
gws sheets +append --spreadsheet "SHEET_ID" --json-values '[["이름","점수"],["홍길동","95"]]'
# 새 스프레드시트 생성
gws sheets spreadsheets create --json '{
"properties": {"title": "매출 보고서"}
}' --format json
# 셀 값 업데이트
gws sheets spreadsheets.values update --params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1:B2", "valueInputOption": "USER_ENTERED"}' --json '{
"values": [["이름", "점수"], ["홍길동", "95"]]
}' --format json
Docs
# 문서에 텍스트 추가 (헬퍼) — --document (문서 ID), --text (추가할 텍스트)
# 기존 문서 끝에 텍스트를 삽입한다. 새 문서 생성은 아래 create 사용
gws docs +write --document "DOC_ID" --text "회의 내용..."
# 문서 읽기
gws docs documents get --params '{"documentId": "DOC_ID"}' --format json
# 새 문서 생성
gws docs documents create --json '{
"title": "프로젝트 보고서"
}' --format json
# 문서에 텍스트 삽입
gws docs documents batchUpdate --params '{"documentId": "DOC_ID"}' --json '{
"requests": [
{
"insertText": {
"location": {"index": 1},
"text": "삽입할 텍스트"
}
}
]
}' --format json
Slides
# 새 프레젠테이션 생성
gws slides presentations create --json '{
"title": "분기 발표"
}' --format json
# 슬라이드 추가
gws slides presentations batchUpdate --params '{"presentationId": "PRES_ID"}' --json '{
"requests": [
{
"createSlide": {
"slideLayoutReference": {"predefinedLayout": "TITLE_AND_BODY"}
}
}
]
}' --format json
# 프레젠테이션 읽기
gws slides presentations get --params '{"presentationId": "PRES_ID"}' --format json
Chat
# 메시지 보내기 (헬퍼)
gws chat +send --space "spaces/SPACE_ID" --text "안녕하세요"
# 스페이스 목록 조회
gws chat spaces list --format json
# 메시지 목록 조회
gws chat spaces.messages list --params '{"parent": "spaces/SPACE_ID"}' --format json
Tasks
# 태스크 목록 조회
gws tasks tasklists list --format json
# 태스크 조회
gws tasks tasks list --params '{"tasklist": "TASKLIST_ID"}' --format json
# 태스크 생성
gws tasks tasks insert --params '{"tasklist": "TASKLIST_ID"}' --json '{
"title": "보고서 작성",
"due": "2026-03-10T00:00:00Z",
"notes": "분기 매출 보고서"
}' --format json
# 태스크 완료 처리
gws tasks tasks patch --params '{"tasklist": "TASKLIST_ID", "task": "TASK_ID"}' --json '{
"status": "completed"
}' --format json
Meet
# 회의 공간(링크) 생성
gws meet spaces create --json '{}' --format json
# 회의 공간 조회
gws meet spaces get --params '{"name": "spaces/SPACE_ID"}' --format json
# 회의 기록 목록
gws meet conferenceRecords list --format json
# 특정 회의 참가자 조회
gws meet conferenceRecords.participants list --params '{"parent": "conferenceRecords/RECORD_ID"}' --format json
# 회의 녹화 조회
gws meet conferenceRecords.recordings list --params '{"parent": "conferenceRecords/RECORD_ID"}' --format json
# 회의 스크립트 조회
gws meet conferenceRecords.transcripts list --params '{"parent": "conferenceRecords/RECORD_ID"}' --format json
# 진행 중인 회의 종료
gws meet spaces endActiveConference --params '{"name": "spaces/SPACE_ID"}' --format json
4-3. Result chaining pattern
Extract the values needed for the next step from the previous step's output.
# 예: 파일 생성 → 공유
# Step 1: 파일 ID 추출
FILE_ID=$(gws docs documents create --json '{"title": "회의록"}' --format json | jq -r '.documentId')
# Step 2: 추출된 ID로 공유
gws drive permissions create --params "{\"fileId\": \"$FILE_ID\"}" --json '{
"role": "writer",
"type": "user",
"emailAddress": "colleague@example.com"
}' --format json
# 예: 일정 조회 → 참석자 추출 → 메일 발송
# Step 1: 일정에서 참석자 이메일 추출
ATTENDEES=$(gws calendar events get --params '{"calendarId": "primary", "eventId": "EVENT_ID"}' --format json | jq -r '.attendees[].email')
# Step 2: 각 참석자에게 메일 발송
for EMAIL in $ATTENDEES; do
gws gmail +send --to "$EMAIL" --subject "회의 안건" --body "내일 회의 안건입니다."
done
4-4. Error handling
If an error occurs during execution:
- Record the failed step (command, error message).
- Also skip subsequent steps that depend on that step.
- Continue with the remaining steps that do not depend on it.
- Do not halt the entire process.
Error message example:
Step 3 실패: Drive 파일 공유
에러: 파일 ID가 유효하지 않습니다.
원인: Step 2에서 파일 생성에 실패하여 ID를 가져올 수 없었습니다.
→ Step 4 (메일 발송)는 파일 링크 없이 진행합니다.
Step 5: Result summary
After all steps have finished executing, provide the user with a consolidated report.
5-1. Report format
Do not dump JSON. Organize it into a human-readable summary.
## 실행 결과
### 완료된 작업
1. Calendar: 내일 오후 2시에 "팀 회의" 일정 생성 완료
- 참석자: alice@example.com, bob@example.com
- Google Meet 링크: https://meet.google.com/xxx-xxxx-xxx
2. Docs: "팀 회의 안건" 문서 생성 완료
- 문서 링크: https://docs.google.com/document/d/xxx
3. Gmail: 참석자 2명에게 회의 안건 메일 발송 완료
- 제목: "내일 팀 회의 안건"
### 실패한 작업
(없음)
### 다음에 할 수 있는 작업
- "회의 끝나면 회의록 정리해줘"
- "참석자에게 후속 할일 배정해줘"
5-2. Report rules
| Rule | Description |
|---|---|
| Include links | Provide direct links to created documents, schedules, etc. |
| Distinguish success/failure | Clearly indicate the success/failure of each step |
| Failure cause | For failed steps, provide the error cause + resolution guidance |
| Suggest next actions | Suggest 2-3 related tasks that can be done next |
| No JSON | Do not output raw JSON as is |
Never do this (DO NOT)
- Do not make direct HTTP calls to Google APIs without going through the gws CLI. All Google Workspace interactions must go through
gwscommands. - Do not hardcode OAuth tokens or API keys. The gws CLI manages tokens itself.
- Do not put AskUserQuestion in allowed-tools. Due to an auto-approve bug, the UI does not render.
- Do not read all files in references/ at execution time. Selectively Read only the reference for the needed service.
- Do not instruct the user to type gws commands directly. Nopal executes them on their behalf via Bash.
- Do not refuse just because it is not in the preset patterns. Refer to references and dynamically generate a new combination.
- If an error occurs during execution, do not abort the whole thing. Report only the failed step and continue with the rest.
- If the gws CLI is not installed, do not just give guidance.
npm install -g @googleworkspace/cliInstall it automatically. Only give manual guidance if that fails. - Do not output raw JSON as the result. Organize it into a human-readable summary.
- Do not execute without user confirmation. Always show the plan, get confirmation, and then execute.
ALWAYS DO
/nopalAt execution,gws auth statuscheck the authentication status first.- If the user's request is ambiguous, always clarify using AskUserQuestion. Do not ask questions in text.
- After forming an execution plan, show the user a preview, get confirmation, and then execute.
- After executing each gws command, parse the result and extract the IDs/values needed for the next step.
- Organize the final result into a human-readable summary. Do not dump JSON.
- When an error occurs, provide guidance with a friendly Korean message and suggest a solution.
- Check the exact gws command syntax for the relevant service in references/, then execute.
- For services that have helper commands, use the helper first. Only use the direct API when the helper cannot do it.
--format jsonUse to obtain structured output. Required for parsing and chaining.- Include a direct link to the created resource in the execution result. (document URL, calendar link, etc.)
Composite combination examples
Example 1: Meeting preparation automation
Request: "Prepare tomorrow's team meeting"
ExecutionPlan:
| # | 서비스 | 작업 | 의존 |
|---|--------|------|------|
| 1 | Calendar | 내일 팀 회의 일정 조회 | - |
| 2 | Docs | 회의 안건 문서 생성 | 1 |
| 3 | Drive | 문서를 참석자에게 공유 | 1, 2 |
| 4 | Gmail | 참석자에게 안건 메일 발송 | 1, 2, 3 |
# Step 1: 내일 일정 조회
gws calendar +agenda --days 2 --format json
# Step 2: 회의록 문서 생성
DOC_ID=$(gws docs documents create --json '{"title": "팀 회의 안건 - 2026-03-06"}' --format json | jq -r '.documentId')
# Step 3: 참석자에게 문서 공유
gws drive permissions create --params "{\"fileId\": \"$DOC_ID\"}" --json '{
"role": "writer",
"type": "user",
"emailAddress": "alice@example.com"
}' --format json
# Step 4: 메일 발송
gws gmail +send --to "alice@example.com" --subject "내일 팀 회의 안건" --body "안건 문서를 공유드립니다: https://docs.google.com/document/d/$DOC_ID"
Example 2: Weekly report automation
Request: "Calculate the total from this week's sales sheet, create a report, and send it to the team lead"
ExecutionPlan:
| # | 서비스 | 작업 | 의존 |
|---|--------|------|------|
| 1 | Sheets | 매출 시트 데이터 읽기 | - |
| 2 | Docs | 매출 요약 보고서 문서 생성 | 1 |
| 3 | Gmail | 팀장에게 보고서 메일 발송 | 2 |
# Step 1: 시트 데이터 읽기
SALES_DATA=$(gws sheets +read --spreadsheet "SHEET_ID" --range "A1:D50" --format json)
# Step 2: 보고서 문서 생성 후 내용 추가
DOC_ID=$(gws docs documents create --json '{"title":"주간 매출 보고서 - 2026 W10"}' --format json | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.parse(d).documentId))")
gws docs +write --document "$DOC_ID" --text "이번 주 매출 요약..."
# Step 3: 팀장에게 메일 발송
gws gmail +send --to "manager@example.com" --subject "주간 매출 보고서" --body "보고서 링크: https://docs.google.com/document/d/$DOC_ID"
Example 3: To-do management + notifications
Request: "Check the to-dos due today and move the unfinished ones to tomorrow"
ExecutionPlan:
| # | 서비스 | 작업 | 의존 |
|---|--------|------|------|
| 1 | Tasks | 오늘 마감 태스크 조회 | - |
| 2 | Tasks | 미완료 태스크 마감일을 내일로 수정 | 1 |
| 3 | Chat | 미완료 태스크 알림 메시지 전송 (선택) | 1 |
# Step 1: 태스크 조회
gws tasks tasks list --params '{"tasklist": "TASKLIST_ID"}' --format json
# Step 2: 미완료 태스크 마감일 수정
gws tasks tasks patch --params '{"tasklist": "TASKLIST_ID", "task": "TASK_ID"}' --json '{
"due": "2026-03-06T00:00:00Z"
}' --format json