Setup
Use when starting a new project with Maestro, or when the .maestro/ directory does not exist yet and needs initializing. Interactively collects project context, lets you choose a work-item backend (files/gitea/github/gitlab/linear/jira), and bootstraps it.From its SKILL.md
npx -y skills add owebboy/maestro --skill setupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
21.5 KB, as published. Nobody here has run it
Project Setup
Initialize project context through interactive Q&A. Creates the .maestro/ directory with foundational documents that inform all other workflow skills, and copies package assets into it.
Pre-flight
- Check if
.maestro/already exists:- If
.maestro/context/product.mdexists: ask whether to resume or reinitialize - If
.maestro/setup_state.jsonexists with incomplete status: offer to resume
- If
- Detect project type:
- Greenfield: no .git, no package.json/go.mod/requirements.txt/Cargo.toml, no src/
- Brownfield: any of the above exist — analyze existing code to pre-populate answers
Interactive Q&A
Rules: Ask ONE question per turn. Wait for response. Offer 2-3 suggested answers plus "Type your own." Ask no more than 5 questions per section; some sections cap lower, as noted in each section heading. Save progress to .maestro/setup_state.json after each step.
Section 1: Product (max 5 questions)
- Project Name — infer from directory name or package files
- Description — one-sentence summary
- Problem Statement — what problem this solves
- Target Users — who benefits
- Key Goals (optional) — 2-3 goals
Section 2: Guidelines (max 3 questions)
- Voice and Tone — professional / friendly / concise / other
- Design Principles — simplicity / performance / DX / safety / other
Section 3: Tech Stack (max 5 questions)
For brownfield: scan for package.json, go.mod, requirements.txt, Cargo.toml first. Present findings and ask for confirmation.
- Primary Language(s)
- Frontend Framework (if applicable)
- Backend Framework (if applicable)
- Database (if applicable)
- Infrastructure — deployment target
Section 4: Workflow (max 4 questions)
- TDD Strictness — strict (tests before code) / moderate (encouraged) / flexible
- Commit Strategy — conventional commits / descriptive / squash per task
- Code Review — required for all / required for non-trivial / optional
- Verification Checkpoints — after each phase / after each task / at track completion
Section 5: Style Guides (max 2 questions)
- Languages — which style guides to generate (detect from tech stack)
- Existing Configs — incorporate .eslintrc, .prettierrc, etc.?
Artifact Generation
After Q&A, perform two steps in order:
Step A: Copy package assets
Copy package assets from assets/maestro/ into .maestro/. These are idempotent — overwrite CONTRACT.md and all files under adapters/ on every run (they are package-managed); NEVER overwrite .maestro/config.json if it already exists (it is project-owned).
| Source (package) | Destination |
|---|---|
assets/maestro/CONTRACT.md | .maestro/CONTRACT.md |
assets/maestro/adapters/ (whole directory) | .maestro/adapters/ (all profiles, overwrite each) |
assets/maestro/config.template.json | .maestro/config.json (only if not already present) |
Copying all adapter profiles now (not just the chosen one) means switching backends later requires no re-copy.
Step B: Generate context files
Generate these files populated with Q&A answers:
| File | Purpose |
|---|---|
.maestro/context/product.md | Product vision, problem, users, goals |
.maestro/context/guidelines.md | Voice, tone, design principles |
.maestro/context/tech-stack.md | Languages, frameworks, infra, dependencies |
.maestro/context/workflow.md | TDD policy, commits, reviews, verification rules |
.maestro/context/styleguides/<language>.md | One file per language selected in Section 5 (e.g. markdown, bash, json) |
Also create the following empty directories and files:
| Path | Notes |
|---|---|
.maestro/work/ | Prose artifacts dir (spec.md, design.md, plan.md per item) |
.maestro/inbox.md | Pre-triage scratch (create from template if not present) |
Note: .maestro/items/ and its archived sub-directories are created in the Backend Bootstrap step below (files adapter only).
The inbox template (written only if .maestro/inbox.md does not already exist):
# Inbox
Pre-triage scratch. One bullet per raw item; triage converts these into work items.
## Inbox
Backend Selection and Bootstrap
After Artifact Generation, run the following steps in order. All answers are written to .maestro/config.json (which was just created from the template if it did not exist, or already exists as the project-owned copy).
Step C: Choose a work-item backend
Ask:
Which work-item backend should Maestro use?
- files (default — recommended for solo/local work; no external service required)
- gitea — self-hosted Gitea instance
- github — GitHub.com or GitHub Enterprise
- gitlab — GitLab.com or self-hosted GitLab
- linear — Linear.app (native states; per-team workflow)
- jira — Jira Cloud or Jira Server (native states; per-team workflow)
Record the choice as config.adapter in .maestro/config.json. For linear or jira, both resolve to the linear-jira.md adapter profile (the profile branches internally on config.backend.kind).
Step D: Connection capture (forge and native-tracker backends)
Skip this step if the user chose files — go directly to Step E.
If a forge (gitea, github, or gitlab) was chosen:
-
Repo identity — ask for the repository in
owner/nameform (e.g.acme/myapp). Write toconfig.backend.repo. -
Base URL (self-hosted only — gitea and gitlab) — ask for the instance URL (e.g.
https://gitea.example.com). Write toconfig.backend.url. Skip for github (alwayshttps://api.github.com). -
GitLab project ID (gitlab only) — ask for the project's numeric ID or full path (e.g.
42oracme/myapp); the numeric ID is preferred for the REST API. Write toconfig.backend.project_id. (TheglabCLI resolves the project from repo context, so this is needed only for the MCP and API transports — capture it regardless so any transport works. The gitlab adapter's REST base isconfig.backend.url/api/v4/projects/<project_id>/issues.) -
Transport detection — run the shared recipe (see below) to determine which transport is available. Report the resolved transport to the user. If none resolves, print the exact auth command for each transport and STOP — do not continue setup and do not fall back to the files adapter.
Backend-specific detection values:
Backend MCP prefix CLI binary CLI auth check REST base giteamcp__gitea__teatea login list(non-empty)config.backend.url/api/v1/repos/<owner>/<repo>githubmcp__github__ghgh auth statushttps://api.github.com/repos/<owner>/<repo>gitlabmcp__gitlab__glabglab auth statusconfig.backend.url/api/v4/projects/<id>/issuesTransport detection recipe (MCP > CLI > API):
- If
config.transportis already set, use it (skip detection). - MCP: if my available tools include
mcp__<prefix>__*for this backend → resolved =mcp. - CLI: else if
command -v <cli>succeeds AND the CLI reports an authenticated session → resolved =cli. - API: else if
config.backend.tokenor the backend's standard env var is set → resolved =api. - None: STOP. Tell the user exactly which of MCP / CLI / API to set up, with the one-line auth command for each. Never silently fall back to
files.
- If
-
Pin transport (optional) — offer the user the option to pin the resolved transport by setting
config.transportinconfig.json. If they decline, leaveconfig.transportunset (detection runs each session).
If linear was chosen:
-
Linear MCP check — check whether
mcp__linear__*tools are available in this session. If yes, confirm the connection is live (no further credentials needed for MCP transport). If MCP is not available, continue to credential capture below. -
Team key — ask:
What is your Linear team key (e.g.
ENG,BACKEND)? This is the short identifier shown in Linear → Settings → Teams.Write to
config.backend.team. -
API key (only if MCP is not available) — ask:
Paste your Linear API key (Settings → Security → Personal API keys). Leave blank if you will set
LINEAR_API_KEYas an environment variable.Write to
config.backend.tokenif provided (or record that the env var path will be used). -
Write backend block — write to
config.backend:{ "kind": "linear", "team": "<team key>", "token": "<key if captured>" }Omit
tokenif using the env var path. -
Transport detection (Linear — MCP or API only; no CLI):
- If
config.transportis already set, use it (skip detection). - MCP: if my available tools include
mcp__linear__*→ resolved =mcp. - API: else if
config.backend.tokenor$LINEAR_API_KEYis set → resolved =api(Linear GraphQL athttps://api.linear.app/graphql). - None: STOP. Tell the user to either connect the Linear MCP server or supply an API key. Never fall back to
files. Do NOT attempt CLI detection — Linear has no official CLI.
- If
-
Pin transport (optional) — offer to pin via
config.transport. If declined, leave unset.
If jira was chosen:
-
Jira MCP check — check whether
mcp__atlassian__*ormcp__jira__*tools are available. If yes, confirm connection is live. -
Site URL — ask:
What is your Jira site URL (e.g.
https://yourcompany.atlassian.netor your self-hosted URL)?Write to
config.backend.url. -
Project key — ask:
What is your Jira project key (e.g.
ENG,PROJ)?Write to
config.backend.project. -
Credentials (only if MCP is not available) — ask:
Provide your Jira email and API token (Jira → Account Settings → Security → API tokens), separated by a newline, or leave blank if you will use the
jiraCLI (which handles auth separately) or setJIRA_USER_EMAIL+JIRA_API_TOKENas environment variables.Write
config.backend.emailandconfig.backend.tokenif provided. -
Write backend block — write to
config.backend:{ "kind": "jira", "url": "<site url>", "project": "<project key>" }Include
emailandtokenif captured. -
Transport detection (Jira — MCP > CLI > API):
- If
config.transportis already set, use it (skip detection). - MCP: if my available tools include
mcp__atlassian__*ormcp__jira__*→ resolved =mcp. - CLI: else if
command -v jirasucceeds ANDjira mereturns a valid user → resolved =cli. - API: else if
config.backend.tokenor$JIRA_API_TOKENis set → resolved =api(Jira REST at<url>/rest/api/3). - None: STOP. Tell the user to set up one of: Jira MCP server,
jiraCLI (ankitpokhrel/jira-cli), or Jira API token ($JIRA_API_TOKEN). Never fall back tofiles.
- If
-
Pin transport (optional) — offer to pin via
config.transport. If declined, leave unset.
Step E: Label bootstrap and state discovery
For files backend:
Create the following directory structure (idempotent — skip any paths that already exist):
| Path | Notes |
|---|---|
.maestro/items/ | Files-adapter registry root |
.maestro/items/archived/done/ | Archived terminal: done |
.maestro/items/archived/wont-fix/ | Archived terminal: wont-fix |
.maestro/items/archived/deferred/ | Archived terminal: deferred |
.maestro/items/archived/duplicate/ | Archived terminal: duplicate |
No label bootstrap is needed for the files adapter.
For forge backends (gitea, github, gitlab):
Run the chosen adapter's ## Label bootstrap procedure from .maestro/adapters/<adapter>.md. This is idempotent — it lists existing labels first and creates only those that are missing; it is safe to re-run at any time.
Use the transport resolved in Step D for the bootstrap calls (MCP / CLI / API as determined).
For native-tracker backends (linear, jira) — state discovery + statusMap builder:
Instead of label bootstrap, perform state discovery and build config.statusMap and config.fieldMap. Use the transport resolved in Step D for all discovery calls.
E.1 Discover native workflow states
- Linear: call
mcp__linear__list_issue_statuses(MCP) or queryworkflowStates(filter: { team: { key: { eq: "<team>" } } }) { nodes { id name type } }via the Linear GraphQL API. - Jira: call the Jira MCP list-statuses tool (MCP), or run
jira project list-statuses <project>(CLI), or GET<url>/rest/api/3/project/<project>/statuses(API). Extract the unique status names from all issue types.
Collect the full list of native state names and display them to the user:
I discovered the following workflow states on your team's board:
[list the discovered state names]
E.2 Propose statusMap
Using the chosen adapter profile's default canonical→native state table and tier-3 synonym sets (referenced below), match against the discovered states and propose a mapping for all 10 canonical statuses. Present it as an editable table:
| Canonical status | Proposed native state | Source |
|---|---|---|
inbox | <proposed> | default table / fuzzy match / no match |
triaged | <proposed> | … |
reviewed | <proposed> | … |
planned | <proposed> | … |
in-progress | <proposed> | … |
in-review | <proposed> | … |
done | <proposed> | … |
wont-fix | <proposed> | … |
deferred | <proposed> | … |
duplicate | <proposed> | … |
The tier-2 default table and the tier-3 fuzzy/synonym sets live in the chosen adapter profile (.maestro/adapters/linear-jira.md) — its "Default canonical → native state table" and "Four-tier resolution rule" sections own the canonical copy. Read them there and apply the write path: tier-1 statusMap (none yet at setup) → tier-2 default table → tier-3 fuzzy/synonym match against the discovered states → tier-4 ask. Do not restate the table or synonym sets here, so the two files cannot drift.
Ask:
Does this statusMap look correct? Reply with any corrections in the form
<canonical>: <native state name>, or press Enter to accept as-is.
Apply user corrections and persist the final mapping as config.statusMap in config.json.
E.3 Handle unmapped canonical statuses
For any canonical status where no matching native state was found (proposed native = none), flag it:
The following canonical statuses have no native equivalent on your board:
<list>. For each, choose: a) Map to nearest state (I'll suggest one) — skills that set this status will use that state instead b) Leave unmapped — skills that would set it will fall back to the adjacent canonical state
For each unmapped status, wait for the user's choice and record it in config.statusMap (map-to-nearest: write the chosen native state name; leave-unmapped: write null for that key).
E.4 fieldMap builder
Priority discovery:
- Linear: native priorities are fixed (0 = No Priority, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low).
- Jira: call the Jira MCP priorities tool (MCP), or GET
<url>/rest/api/3/priority(API). Display the discovered priority names.
Issue type discovery:
- Linear: issue types are not distinct in Linear (all issues are the same type); set
config.fieldMap.type = null. - Jira: call the Jira MCP issue-types tool (MCP), or
jira issue list-types(CLI), or GET<url>/rest/api/3/project/<key>/issuetypes(Jira Cloud; or<url>/rest/api/3/issuetypeas the server fallback) (API). Display the discovered types.
Propose config.fieldMap:
{
"priority": {
"P1": "<native urgent/highest priority>",
"P2": "<native high priority>",
"P3": "<native medium priority>"
},
"type": "<default issue type for new items, or null for Linear>"
}
Ask:
Does this fieldMap look correct? Reply with any corrections, or press Enter to accept.
Apply corrections and persist as config.fieldMap in config.json.
E.5 Persist stateCache
After confirming the statusMap (E.2/E.3) and fieldMap (E.4), populate config.backend.stateCache in .maestro/config.json. The cache shape is owned by the adapter profile's "What /setup writes" section (backend.stateCache): Linear is name → { id, type }; Jira is name → { id, transitionId }, both keyed by native state name. The adapter's set_status op reads this cache to resolve native state IDs (and Jira transition IDs) without re-querying the API on every status change.
How to populate it at setup:
- Linear: the states discovered in E.1 already carry
idandtype— write one entry per state. - Jira: transition IDs are not returned by the statuses endpoint. Fetch them with
GET <url>/rest/api/3/issue/<sample-issue-key>/transitions(using any existing issue) or the equivalent MCP/CLI call. If the project has no issues yet, store the statusids and leavetransitionIdasnull— the adapter resolves it on first use (itsset_statusop handles the cache miss).
Step F: Capture mode
Ask:
How should Maestro capture fleeting ideas and raw notes?
- local (default — items go to
.maestro/inbox.mdas bullets; triage converts them)- backend — capture creates a draft issue with
status:inboxdirectly in the chosen backend
Record as config.captureMode in .maestro/config.json.
State Management
Track progress in .maestro/setup_state.json. Get the current timestamp by running date -u +%Y-%m-%dT%H:%M:%SZ — do not assume you know it.
{
"status": "in_progress|complete",
"project_type": "greenfield|brownfield",
"current_section": "product|guidelines|tech_stack|workflow|styleguides|backend",
"current_question": 1,
"completed_sections": [],
"started_at": "ISO_TIMESTAMP",
"last_updated": "ISO_TIMESTAMP"
}
Setup is complete only after package assets are copied, all context sections are done, AND the backend flow (Steps C–F) is complete. completed_sections must contain all six: product, guidelines, tech_stack, workflow, styleguides, backend.
Artifact Templates
The context files are not parsed by downstream skills, so each only needs the section structure below — populate every section from the Q&A answers, and keep them terse and scannable (tables, bullets, and checklists over prose).
product.md
## Name · ## Description · ## Problem Statement · ## Target Users · ## Key Goals
guidelines.md
## Voice & Tone · ## Design Principles · ## Applying These
tech-stack.md
## Primary Languages · ## Frontend / Backend / Database · ## Distribution & Infrastructure · ## Key Layout · ## Dependencies · ## Validation Commands
workflow.md
## TDD / Verification Discipline · ## Commit Strategy · ## Code Review · ## Verification Checkpoints · ## Work Item Lifecycle
context/styleguides/
One file per language chosen in Section 5 (e.g. markdown.md, bash.md, json.md). Each file: a one-line intro plus ## Formatting, ## Conventions, and ## Anti-patterns sections with project-specific rules.
Completion
For the files backend, print:
Setup complete!
Created: .maestro/CONTRACT.md
.maestro/adapters/ (files.md + all adapter profiles)
.maestro/config.json (adapter: files, captureMode: <chosen>)
.maestro/context/{product,guidelines,tech-stack,workflow}.md
.maestro/context/styleguides/
.maestro/work/
.maestro/items/ (+ archived/{done,wont-fix,deferred,duplicate}/)
.maestro/inbox.md
Next: Run /track-new in Claude Code or $track-new in Codex to create your first track.
For a forge backend (gitea / github / gitlab), print:
Setup complete!
Created: .maestro/CONTRACT.md
.maestro/adapters/ (files.md + all adapter profiles)
.maestro/config.json (adapter: <chosen>, repo: <owner/name>, captureMode: <chosen>)
.maestro/context/{product,guidelines,tech-stack,workflow}.md
.maestro/context/styleguides/
.maestro/work/
.maestro/inbox.md
Bootstrap: <N> labels created in <backend> (transport: <mcp|cli|api>)
Next: Run /track-new in Claude Code or $track-new in Codex to create your first track.
For a native-tracker backend (linear / jira), print:
Setup complete!
Created: .maestro/CONTRACT.md
.maestro/adapters/ (files.md + all adapter profiles)
.maestro/config.json (adapter: <linear|jira>, team/project: <key>, captureMode: <chosen>)
.maestro/context/{product,guidelines,tech-stack,workflow}.md
.maestro/context/styleguides/
.maestro/work/
.maestro/inbox.md
Discovery: <N> native states discovered (transport: <mcp|cli|api>)
statusMap: <N> canonical statuses mapped, <N> unmapped
fieldMap: priority (P1/P2/P3 → native), type: <type or null>
Next: Run /track-new in Claude Code or $track-new in Codex to create your first track.
Resume
If --resume or resuming from state: skip completed sections, resume from current_section + current_question, and verify previously created files still exist. If a file from a completed section is missing, warn the user and re-run that section to regenerate it before continuing. If the backend section is incomplete, resume from the last unfinished step (C, D, E, or F).