From agentic-gtd
This skill should be used when syncing local tasks/*.md into a GitHub Project (one-way push), or when initializing the GitHub Project and its fields via --init.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agentic-gtd:github-syncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- **One-way push only**: data flows `tasks/*.md` → GitHub Project. Never pull. Never conflict-resolve. Never delete.
tasks/*.md → GitHub Project. Never pull. Never conflict-resolve. Never delete.tasks/inbox.md is ALWAYS excluded by basename match — identical exclusion rule as gtd-prioritization.^- \[([ x])\] (.+)$ — captures checkbox state (space = incomplete, x = complete) AND the task content.Status field.| Markdown source | GitHub Project field | Field type | Values / notes |
|---|---|---|---|
| filename stem | Domain | single-select | fulltime, side-projects, open-source, knowledge |
prio: tag | Priority | single-select | fulltime, trust, side, long, short, tedious |
due: tag | Due | date | ISO YYYY-MM-DD; empty if absent/malformed |
checkbox state [ ] / [x] | Status | single-select | Todo (space) / Done (x) |
| task title (before first tag) | item title | built-in title | trimmed, internal whitespace collapsed |
project: tag | Project/Area | text | omit field write if tag absent |
Missing prio → set Priority to none + emit warning. Missing due → leave Due field empty + emit warning. Unknown domain (filename not in the four known domains) → skip item entirely + emit warning.
Composite dedupe key = domain + normalizedTitle
Title normalization: trim leading/trailing whitespace, collapse all internal whitespace runs to single space. Do NOT lowercase — title casing is preserved.
Algorithm:
dedupe_key → {itemId, fieldValues}.dedupe_key.Known limitation (document in bold in output): Renaming a task in markdown creates a duplicate: the old GitHub item is orphaned. One-way push never deletes GitHub items. To remove an orphan, delete it manually in the GitHub Project UI.
Listing and creation (classic operations):
gh project item-list <number> --owner <owner> --format jsongh project item-create <number> --owner <owner> --title "<title>" → captures item IDmcp__personal-github__* tools where they support these operationsProjects v2 field value mutations REQUIRE GraphQL — gh CLI item-edit does not support custom fields reliably:
# Create draft issue
mutation {
addProjectV2DraftIssue(input: {projectId: "<projectId>", title: "<title>"}) {
projectItem { id }
}
}
# Set single-select field
mutation {
updateProjectV2ItemFieldValue(input: {
projectId: "<projectId>",
itemId: "<itemId>",
fieldId: "<fieldId>",
value: { singleSelectOptionId: "<optionId>" }
}) {
projectV2Item { id }
}
}
# Set date field
mutation {
updateProjectV2ItemFieldValue(input: {
projectId: "<projectId>",
itemId: "<itemId>",
fieldId: "<fieldId>",
value: { date: "YYYY-MM-DD" }
}) {
projectV2Item { id }
}
}
# Set text field
mutation {
updateProjectV2ItemFieldValue(input: {
projectId: "<projectId>",
itemId: "<itemId>",
fieldId: "<fieldId>",
value: { text: "<value>" }
}) {
projectV2Item { id }
}
}
Invoke GraphQL via: gh api graphql -f query='<mutation>'
Field IDs and option IDs are opaque strings — resolve them once during --init and cache in config. Never hardcode.
Resolve owner: query gh api graphql -f query='{ viewer { id login } }' or call mcp__personal-github__get_me. Record login and node id.
Check config: read .agentic-gtd.local.md at repo root. If projectId key is present and non-empty → report "already initialized (projectId: <id>)" and exit without any mutations.
Create project: run gh project create --owner @me --title "Agentic GTD" (private by default). Capture the project number and node ID from output.
Create custom fields via GraphQL createProjectV2Field mutation:
Priority — single-select, options in ladder order: fulltime, trust, side, long, short, tediousDomain — single-select, options: fulltime, side-projects, open-source, knowledgeDue — date fieldProject/Area — text fieldStatus — built-in field (do not recreate). Ensure Todo and Done options exist; add Done if missing.Resolve all field IDs and option IDs: query the project's field list via GraphQL to get each fieldId and each option's id.
Write config: save all IDs to .agentic-gtd.local.md (see Config File section).
Update .gitignore: append .agentic-gtd.local.md to the repo root .gitignore if not already present.
Location: .agentic-gtd.local.md at repo root (not inside .claude/).
This file is human-readable with a YAML-style key:value block:
# Agentic GTD — Local Config
# Auto-generated by /sync-github --init. Do not commit (add to .gitignore).
projectId: PVT_xxxxxxxxxxxx
projectNumber: 1
owner: josixwang
fields:
Priority:
fieldId: PVTSSF_xxxxxxxxxxxx
type: single_select
options:
fulltime: xxxxxxxx
side: xxxxxxxx
trust: xxxxxxxx
long: xxxxxxxx
short: xxxxxxxx
tedious: xxxxxxxx
Domain:
fieldId: PVTSSF_xxxxxxxxxxxx
type: single_select
options:
fulltime: xxxxxxxx
side-projects: xxxxxxxx
open-source: xxxxxxxx
knowledge: xxxxxxxx
Status:
fieldId: PVTSSF_xxxxxxxxxxxx
type: single_select
options:
Todo: xxxxxxxx
Done: xxxxxxxx
Due:
fieldId: PVTF_xxxxxxxxxxxx
type: date
Project/Area:
fieldId: PVTF_xxxxxxxxxxxx
type: text
Note: this file contains project-specific IDs and must not be committed. The .gitignore line is added during --init.
Read config: parse .agentic-gtd.local.md. If file missing or projectId is empty/absent:
FATAL: GitHub Project not initialized. Run /sync-github --init first.
Exit immediately. Zero mutations.
Preflight auth: run gh auth status or call mcp__personal-github__get_me. On failure:
FATAL: GitHub auth failed — <exact error message>. Run: gh auth login
Exit immediately. Zero mutations.
Parse tasks: read all tasks/*.md except tasks/inbox.md. Parse all lines matching ^- \[([ x])\] (.+)$. Extract and normalize all tags.
Fetch existing items: gh project item-list <number> --owner <owner> --format json. Build dedupe map: normalizedKey → {itemId, currentFieldValues}.
Resolve create vs update: for each parsed task, compute dedupe key; look up in map.
Execute (skip if --dry-run):
addProjectV2DraftIssue → get itemId → set all field values via updateProjectV2ItemFieldValue.Emit sync report.
Sync complete (2026-06-08T12:00:00Z):
Created: 5
Updated: 12
Completed (Status=Done): 3
Skipped: 0
Warnings:
- [tasks/knowledge.md: "some task"] Missing prio — Priority field left empty
- [tasks/fulltime.md: "other task"] Malformed due date: "next week" — Due field left empty
On dry-run, every line is prefixed with [DRY-RUN] and the final line reads:
[DRY-RUN] No mutations were made.
ABORT: GraphQL error on item "Task title": <error>
Mutated before abort: Created 5, Updated 2
Prefer ONE transport for identity resolution (use gh CLI to resolve owner; record owner login in config). At preflight, warn if gh auth status shows a different user than the recorded owner:
Warning: gh authenticated as "other-user", but config owner is "josixwang". Proceeding may create items under the wrong account.
npx claudepluginhub josix/agentic-gtdCreates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.