SuperAPI AI Directory

nopal-orchestrate

Office & productivity

View repository

Updated 10-15 20:06

About this skill

Nopal Orchestration Engine

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?":

  1. gws calendar +agenda --days 3 --format json execution
  2. Extract meeting list from the result
  3. 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, +agenda etc. read-only
  • create, send, insert, update, delete, trash, modify, patch etc. 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:

  1. Record the failed step (command, error message).
  2. Also skip subsequent steps that depend on that step.
  3. Continue with the remaining steps that do not depend on it.
  4. 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)

  1. Do not make direct HTTP calls to Google APIs without going through the gws CLI. All Google Workspace interactions must go through gws commands.
  2. Do not hardcode OAuth tokens or API keys. The gws CLI manages tokens itself.
  3. Do not put AskUserQuestion in allowed-tools. Due to an auto-approve bug, the UI does not render.
  4. Do not read all files in references/ at execution time. Selectively Read only the reference for the needed service.
  5. Do not instruct the user to type gws commands directly. Nopal executes them on their behalf via Bash.
  6. Do not refuse just because it is not in the preset patterns. Refer to references and dynamically generate a new combination.
  7. If an error occurs during execution, do not abort the whole thing. Report only the failed step and continue with the rest.
  8. 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.
  9. Do not output raw JSON as the result. Organize it into a human-readable summary.
  10. Do not execute without user confirmation. Always show the plan, get confirmation, and then execute.

ALWAYS DO

  1. /nopal At execution, gws auth statuscheck the authentication status first.
  2. If the user's request is ambiguous, always clarify using AskUserQuestion. Do not ask questions in text.
  3. After forming an execution plan, show the user a preview, get confirmation, and then execute.
  4. After executing each gws command, parse the result and extract the IDs/values needed for the next step.
  5. Organize the final result into a human-readable summary. Do not dump JSON.
  6. When an error occurs, provide guidance with a friendly Korean message and suggest a solution.
  7. Check the exact gws command syntax for the relevant service in references/, then execute.
  8. For services that have helper commands, use the helper first. Only use the direct API when the helper cannot do it.
  9. --format jsonUse to obtain structured output. Required for parsing and chaining.
  10. 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