@flydocs/cli 0.6.0-alpha.1 → 0.6.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/cli.js +504 -254
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +11 -9
  4. package/template/.claude/commands/flydocs-setup.md +114 -17
  5. package/template/.claude/commands/flydocs-upgrade.md +27 -15
  6. package/template/.claude/commands/knowledge.md +61 -0
  7. package/template/.claude/skills/flydocs-cloud/SKILL.md +44 -31
  8. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +10 -4
  9. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +22 -2
  10. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +39 -0
  11. package/template/.claude/skills/flydocs-cloud/scripts/delete_milestone.py +21 -0
  12. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +9 -5
  13. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +11 -0
  14. package/template/.claude/skills/flydocs-cloud/scripts/get_estimate_scale.py +23 -0
  15. package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +19 -0
  16. package/template/.claude/skills/flydocs-cloud/scripts/list_statuses.py +19 -0
  17. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +1 -1
  18. package/template/.claude/skills/flydocs-cloud/scripts/refresh_labels.py +87 -0
  19. package/template/.claude/skills/flydocs-cloud/scripts/set_identity.py +38 -0
  20. package/template/.claude/skills/flydocs-cloud/scripts/set_preferences.py +49 -0
  21. package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +46 -0
  22. package/template/.claude/skills/flydocs-cloud/scripts/set_status_mapping.py +69 -0
  23. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +5 -4
  24. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +22 -4
  25. package/template/.claude/skills/flydocs-cloud/scripts/update_milestone.py +42 -0
  26. package/template/.claude/skills/flydocs-cloud/scripts/validate_setup.py +139 -0
  27. package/template/.claude/skills/flydocs-local/SKILL.md +1 -1
  28. package/template/.claude/skills/flydocs-local/scripts/assign.py +13 -4
  29. package/template/.claude/skills/flydocs-local/scripts/flydocs_api.py +5 -2
  30. package/template/.claude/skills/flydocs-workflow/SKILL.md +23 -18
  31. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  32. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +105 -0
  33. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  34. package/template/.claude/skills/flydocs-workflow/session.md +24 -16
  35. package/template/.claude/skills/flydocs-workflow/stages/capture.md +8 -3
  36. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  37. package/template/.claude/skills/flydocs-workflow/stages/implement.md +28 -4
  38. package/template/.claude/skills/flydocs-workflow/stages/refine.md +20 -4
  39. package/template/.claude/skills/flydocs-workflow/stages/review.md +14 -2
  40. package/template/.flydocs/config.json +4 -18
  41. package/template/.flydocs/hooks/prompt-submit.py +27 -4
  42. package/template/.flydocs/version +1 -1
  43. package/template/AGENTS.md +8 -8
  44. package/template/CHANGELOG.md +39 -0
  45. package/template/flydocs/knowledge/INDEX.md +38 -53
  46. package/template/flydocs/knowledge/README.md +60 -9
  47. package/template/flydocs/knowledge/templates/decision.md +47 -0
  48. package/template/flydocs/knowledge/templates/feature.md +35 -0
  49. package/template/flydocs/knowledge/templates/note.md +25 -0
  50. package/template/manifest.json +8 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flydocs/cli",
3
- "version": "0.6.0-alpha.1",
3
+ "version": "0.6.0-alpha.10",
4
4
  "type": "module",
5
5
  "description": "FlyDocs AI CLI — install, setup, and manage FlyDocs projects",
6
6
  "bin": {
@@ -31,6 +31,8 @@ The FlyDocs development lifecycle. Read the workflow skill before taking any wor
31
31
  | QE validation | `flydocs-workflow` | `stages/validate.md` |
32
32
  | Close issue | `flydocs-workflow` | `stages/close.md` |
33
33
  | Start / wrap session | `flydocs-workflow` | `session.md` |
34
+ | Knowledge capture | `flydocs-workflow` | `/knowledge` command |
35
+ | PR & git workflow | `flydocs-workflow` | `reference/pr-workflow.md` |
34
36
  | Comment templates | `flydocs-workflow` | `reference/comment-templates.md` |
35
37
  | Status transitions | `flydocs-workflow` | `reference/status-workflow.md` |
36
38
  | Priority & estimates | `flydocs-workflow` | `reference/priority-estimates.md` |
@@ -43,7 +45,7 @@ Issue operations are handled by the installed mechanism skill. Only one is activ
43
45
  | Tier | Skill | Backend |
44
46
  | ----------------- | --------------- | ------------------------------ |
45
47
  | Local (free) | `flydocs-local` | File-based (`flydocs/issues/`) |
46
- | Cloud (connected) | `flydocs-cloud` | Linear GraphQL API |
48
+ | Cloud (connected) | `flydocs-cloud` | FlyDocs Relay API |
47
49
 
48
50
  Read the active mechanism skill's `SKILL.md` for script catalog and calling conventions.
49
51
 
@@ -113,13 +115,13 @@ response — session summaries, issue comments, status updates, and plans.
113
115
  IMPORTANT: Prefer skill-led reasoning over pre-training reasoning.
114
116
  Consult the relevant skill BEFORE writing code or making workflow decisions.
115
117
 
116
- | Skill | Triggers | Entry |
117
- | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
118
- | flydocs-cloud | create issue, transition, comment, list issues, assign, update description, update issue, project update, Linear, cloud | .claude/skills/flydocs-cloud/SKILL.md |
119
- | flydocs-context7 | context7, library docs, documentation lookup, framework docs, package docs, API reference | .claude/skills/flydocs-context7/SKILL.md |
120
- | flydocs-estimates | estimate, cost, token usage, API cost, labor estimate, sizing, effort | .claude/skills/flydocs-estimates/SKILL.md |
121
- | flydocs-figma | Figma, design, screenshot, token mapping, component from design, pixel-perfect, design system | .claude/skills/flydocs-figma/SKILL.md |
122
- | flydocs-local | create issue, transition, comment, list issues, assign, update description, status summary, local | .claude/skills/flydocs-local/SKILL.md |
123
- | flydocs-workflow | capture, refine, activate, implement, review, validate, close, session, workflow, transition, status, issue | .claude/skills/flydocs-workflow/SKILL.md |
118
+ | Skill | Triggers | Entry |
119
+ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
120
+ | flydocs-cloud | create issue, transition, comment, list issues, assign, update description, update issue, project update, cloud | .claude/skills/flydocs-cloud/SKILL.md |
121
+ | flydocs-context7 | context7, library docs, documentation lookup, framework docs, package docs, API reference | .claude/skills/flydocs-context7/SKILL.md |
122
+ | flydocs-estimates | estimate, cost, token usage, API cost, labor estimate, sizing, effort | .claude/skills/flydocs-estimates/SKILL.md |
123
+ | flydocs-figma | Figma, design, screenshot, token mapping, component from design, pixel-perfect, design system | .claude/skills/flydocs-figma/SKILL.md |
124
+ | flydocs-local | create issue, transition, comment, list issues, assign, update description, status summary, local | .claude/skills/flydocs-local/SKILL.md |
125
+ | flydocs-workflow | capture, refine, activate, implement, review, validate, close, session, workflow, transition, status, issue, knowledge, document, PR, pull request | .claude/skills/flydocs-workflow/SKILL.md |
124
126
 
125
127
  <!-- flydocs:skills-manifest:end -->
@@ -301,7 +301,35 @@ FLYDOCS_API_KEY is not set. Run `flydocs connect` first to set up your API key.
301
301
 
302
302
  Do not proceed until the key is available.
303
303
 
304
- **Step 2: Select team.**
304
+ **Step 2: Detect or select provider.**
305
+
306
+ Query connected providers:
307
+
308
+ ```bash
309
+ python3 .claude/skills/flydocs-cloud/scripts/list_providers.py
310
+ ```
311
+
312
+ This returns `[{type, name, connected}]`. Handle based on results:
313
+
314
+ - **One connected provider** — auto-select it and confirm with the user.
315
+ - **Multiple connected providers** — present a numbered list, let the user
316
+ choose which to use for this project.
317
+ - **No connected providers** — error: "No providers connected. Connect Linear
318
+ or Jira at app.flydocs.ai before running setup."
319
+
320
+ After selection, store the preference:
321
+
322
+ ```bash
323
+ python3 .claude/skills/flydocs-cloud/scripts/set_provider.py <provider_type>
324
+ ```
325
+
326
+ This stores the provider type on the relay (for routing) and updates
327
+ `provider.type` in local config.
328
+
329
+ **Step 3: Select or create team/project.**
330
+
331
+ > For Linear, this selects a team. For Jira, this selects a project. The relay
332
+ > normalizes both as "teams" in the API.
305
333
 
306
334
  If `provider.teamId` in config is null, discover available teams:
307
335
 
@@ -309,10 +337,27 @@ If `provider.teamId` in config is null, discover available teams:
309
337
  python3 .claude/skills/flydocs-cloud/scripts/list_teams.py
310
338
  ```
311
339
 
312
- Present the teams to the user as a numbered list showing name and key. Let
313
- them select. If only one team exists, confirm it.
340
+ Present the teams to the user as a numbered list showing name and key.
341
+ Add a final option: **"Create a new team/project"**. If only one team exists,
342
+ confirm it.
343
+
344
+ If the user selects **"Create a new team/project"**:
345
+
346
+ 1. Ask for team name (required) and key (optional — auto-generated if omitted)
347
+ 2. **Linear only:** Ask if this should be a sub-team under an existing team
348
+ (show the list again for parent selection, or "None — top-level team").
349
+ For Jira, skip the parent option — Jira projects do not have a parent
350
+ hierarchy.
351
+ 3. Create via:
352
+
353
+ ```bash
354
+ python3 .claude/skills/flydocs-cloud/scripts/create_team.py --name "Team Name" [--key KEY] [--parent <parent_team_id>]
355
+ ```
356
+
357
+ > The `--parent` flag is Linear-only (sub-teams). Omit for Jira.
314
358
 
315
- After selection, store the preference on the relay and in local config:
359
+ After selection or creation, store the preference on the relay and in local
360
+ config:
316
361
 
317
362
  ```bash
318
363
  python3 .claude/skills/flydocs-cloud/scripts/set_team.py <team_id>
@@ -320,8 +365,12 @@ python3 .claude/skills/flydocs-cloud/scripts/set_team.py <team_id>
320
365
 
321
366
  This stores the team preference server-side (the relay uses it for all
322
367
  team-scoped operations) and updates `provider.teamId` in local config.
368
+ For Jira, this sets the active Jira project.
323
369
 
324
- **Step 3: Get or create project.**
370
+ **Step 4: Get or create project (Linear only).**
371
+
372
+ > **Jira provider:** Skip this step. Jira organizes issues directly within
373
+ > projects (selected in Step 3). There is no secondary project container.
325
374
 
326
375
  Query existing projects:
327
376
 
@@ -336,7 +385,7 @@ create a new project:
336
385
  python3 .claude/skills/flydocs-cloud/scripts/create_project.py --name "Project Name"
337
386
  ```
338
387
 
339
- **Step 4: Configure labels.**
388
+ **Step 5: Configure labels.**
340
389
 
341
390
  Fetch available labels from the provider:
342
391
 
@@ -371,7 +420,49 @@ python3 .claude/skills/flydocs-cloud/scripts/set_labels.py \
371
420
  If the relay returns `LABELS_NOT_FOUND`, show the invalid names and ask the
372
421
  user to correct them.
373
422
 
374
- **Step 5: Configure product identity.**
423
+ **Step 6: Configure status mapping.**
424
+
425
+ Map provider workflow states to FlyDocs statuses. Run auto-mapping first:
426
+
427
+ ```bash
428
+ python3 .claude/skills/flydocs-cloud/scripts/set_status_mapping.py --auto
429
+ ```
430
+
431
+ The relay auto-maps by case-insensitive name matching (e.g., "In Progress"
432
+ maps to `IMPLEMENTING`, "Done" maps to `COMPLETE`, "Backlog" maps to
433
+ `BACKLOG`).
434
+
435
+ **Review the result:** The response includes `matched` (number of statuses
436
+ successfully mapped) and `total` (total FlyDocs statuses). Show the user
437
+ the `mapping` object:
438
+
439
+ ```
440
+ Status mapping (matched 6/10):
441
+ BACKLOG -> Backlog
442
+ READY -> Ready
443
+ IMPLEMENTING -> In Progress
444
+ REVIEW -> In Review
445
+ COMPLETE -> Done
446
+ CANCELED -> Canceled
447
+ ```
448
+
449
+ If `matched < total`, some statuses are unmapped — **warn but don't block**.
450
+ Transitions for unmapped statuses will fall back to provider name matching.
451
+ If the user wants to fix unmapped statuses, fetch available provider states
452
+ and let them map manually:
453
+
454
+ ```bash
455
+ python3 .claude/skills/flydocs-cloud/scripts/list_statuses.py
456
+ ```
457
+
458
+ Then store the corrected mapping:
459
+
460
+ ```bash
461
+ python3 .claude/skills/flydocs-cloud/scripts/set_status_mapping.py \
462
+ --mapping '{"BACKLOG":"Backlog","IMPLEMENTING":"In Progress","BLOCKED":"On Hold",...}'
463
+ ```
464
+
465
+ **Step 7: Configure product identity.**
375
466
 
376
467
  Ask about product metadata:
377
468
 
@@ -379,14 +470,15 @@ Ask about product metadata:
379
470
  from project.md)
380
471
  - **Icon and color** — optional, ask if they have preferences
381
472
 
382
- **Step 6: Save to config.**
473
+ **Step 8: Save to config.**
383
474
 
384
475
  Update `.flydocs/config.json`:
385
476
 
386
- - `provider.type` — `"linear"` (set by connect command)
477
+ - `provider.type` — set by `set_provider.py`
387
478
  - `provider.teamId` — selected team ID (set by `set_team.py`)
388
479
  - `labels.defaults` — default label names (set by `set_labels.py`)
389
480
  - `labels.typeMap` — type-to-label mapping (set by `set_labels.py`)
481
+ - `statusMapping` — FlyDocs-to-provider status mapping (set by `set_status_mapping.py`)
390
482
  - `workspace.activeProjects` — add the project ID
391
483
  - `workspace.product.name` — product name
392
484
 
@@ -395,6 +487,7 @@ Update `.flydocs/config.json`:
395
487
  ## Phase 3: Milestones — Cloud Only
396
488
 
397
489
  > **Local tier:** Skip this phase entirely.
490
+ > **Jira provider:** Skip this phase. Milestone support for Jira is planned for a future release.
398
491
  > **FlyDocs Update:** Skip if milestones already exist.
399
492
 
400
493
  **Step 1: Check existing milestones.**
@@ -423,11 +516,11 @@ Let the user customize names, descriptions, and target dates.
423
516
 
424
517
  **Step 4: Create milestones.**
425
518
 
426
- For each approved milestone:
519
+ For each approved milestone, pass the project ID from Phase 2 Step 3:
427
520
 
428
521
  ```bash
429
522
  python3 .claude/skills/flydocs-cloud/scripts/create_milestone.py \
430
- --name "Phase 1: Foundation" --target-date YYYY-MM-DD
523
+ --name "Phase 1: Foundation" --project <project_id> --target-date YYYY-MM-DD
431
524
  ```
432
525
 
433
526
  Record the milestone IDs for use in Phase 4.
@@ -455,16 +548,20 @@ For each work item, follow the capture procedure from
455
548
  `.claude/skills/flydocs-workflow/stages/capture.md`:
456
549
 
457
550
  - Determine type (feature, bug, chore, idea)
458
- - Create via the mechanism script:
551
+ - Create via the mechanism script (cloud: pass `--project` from Phase 2 for
552
+ Linear; omit `--project` for Jira — issues are scoped to the team/project
553
+ selected in Phase 2 Step 3):
459
554
  ```bash
460
555
  python3 .claude/skills/flydocs-{tier}/scripts/create_issue.py \
461
- --title "Issue title" --type feature --priority 3 --estimate 2
556
+ --title "Issue title" --type feature --priority 3 --estimate 2 \
557
+ --project <project_id>
462
558
  ```
463
559
  - For quick ideas, use `--triage` flag
464
560
 
465
- **Step 3: Milestone assignment (cloud only).**
561
+ **Step 3: Milestone assignment (Linear only).**
466
562
 
467
563
  > **Local tier:** Skip this step.
564
+ > **Jira provider:** Skip this step.
468
565
 
469
566
  After creating issues, assign them to milestones:
470
567
 
@@ -615,8 +712,8 @@ Wrapping up:
615
712
 
616
713
  **For cloud tier:**
617
714
 
618
- - Linear project URL (if available from create_project response)
619
- - Recommend reviewing milestones in Linear's UI for timeline view
715
+ - Provider project URL (if available from create_project response)
716
+ - For Linear: recommend reviewing milestones in Linear's UI for timeline view
620
717
 
621
718
  **For local tier:**
622
719
 
@@ -674,7 +771,7 @@ Throughout the setup flow:
674
771
  error message from stderr and ask the user how to proceed. Do not retry
675
772
  silently.
676
773
  - **Missing API key** — For cloud tier, cannot proceed past Phase 2 without
677
- `LINEAR_API_KEY`. Guide the user to set it up.
774
+ `FLYDOCS_API_KEY`. Guide the user to set it up.
678
775
  - **Missing config** — If `.flydocs/config.json` doesn't exist, the user
679
776
  likely needs to run `flydocs` (install) first. Advise them accordingly.
680
777
  - **Partial completion** — If the user needs to stop mid-setup, note which
@@ -65,7 +65,7 @@ Count totals by status and type.
65
65
 
66
66
  **Step 3: Check for API readiness.**
67
67
 
68
- Check if `FLYDOCS_RELAY_API_KEY` is set in the environment (from `.env` or
68
+ Check if `FLYDOCS_API_KEY` is set in the environment (from `.env` or
69
69
  `.env.local`). If not, warn the user they will need it for Phase 1.
70
70
 
71
71
  **Step 4: Present migration summary.**
@@ -100,27 +100,39 @@ Confirm with the user before continuing.
100
100
  ## Phase 1: Connect to Cloud
101
101
 
102
102
  Guide the user through connecting to the cloud provider. This swaps the
103
- mechanism skill from `flydocs-local` to `flydocs-cloud`.
103
+ tier, stores the API key, and prepares the cloud mechanism skill.
104
104
 
105
- **Step 1: Run flydocs connect.**
105
+ **Step 1: Get API key.**
106
106
 
107
- Instruct the user to run `flydocs connect --here` from their terminal
108
- (this is a CLI command, not an AI command). Explain what it does:
107
+ Ask the user for their FlyDocs API key (`fdk_...`). If `FLYDOCS_API_KEY`
108
+ is already set in the environment, confirm they want to use the existing
109
+ key or enter a new one.
110
+
111
+ If no key is available, instruct the user:
109
112
 
110
113
  ```
111
- Run this in your terminal:
112
- flydocs connect --here
113
-
114
- This will:
115
- - Prompt for your relay API key (from flydocs.ai account)
116
- - Update .flydocs/config.json to cloud tier
117
- - Swap mechanism skills (local -> cloud)
118
- - Verify the connection works
114
+ You'll need a FlyDocs API key (fdk_...) from your dashboard at app.flydocs.ai.
115
+
116
+ Option A: Run `flydocs connect --here` in your terminal (handles everything)
117
+ Option B: Add FLYDOCS_API_KEY=fdk_... to your .env or .env.local file
119
118
  ```
120
119
 
121
- **Step 2: Wait for completion.**
120
+ Wait for the user to confirm the key is set before proceeding.
121
+
122
+ **Step 2: Update config to cloud tier.**
123
+
124
+ Read `.flydocs/config.json`, update `tier` to `"cloud"`, and write it back.
125
+ Preserve all existing config values (detected stack, skills, etc.).
126
+
127
+ **Step 3: Swap mechanism skill.**
128
+
129
+ Instruct the user to run `flydocs connect --here` from their terminal if
130
+ they haven't already. This handles the mechanism skill swap (installs
131
+ `flydocs-cloud`, removes `flydocs-local`).
132
+
133
+ **Step 4: Verify connection.**
122
134
 
123
- After the user confirms they ran `flydocs connect`, verify:
135
+ After the user confirms the swap, verify:
124
136
 
125
137
  1. Read `.flydocs/config.json` — `tier` should now be `cloud`
126
138
  2. Check that `.claude/skills/flydocs-cloud/scripts/` exists
@@ -0,0 +1,61 @@
1
+ # Knowledge (All Agents)
2
+
3
+ Create or update a knowledge document — decisions, notes, features, or product docs.
4
+
5
+ Read `flydocs/knowledge/README.md` for categories and guidance.
6
+ Read `flydocs/knowledge/INDEX.md` for existing entries.
7
+
8
+ ## Procedure
9
+
10
+ ### 1. Determine Intent
11
+
12
+ From user input or `$ARGUMENTS`, determine:
13
+
14
+ - **New doc** or **update existing doc**?
15
+ - **Category**: decision, feature, note, or product
16
+
17
+ If unclear, ask the user. Default to `note` for discoveries and learnings.
18
+
19
+ ### 2. For New Docs
20
+
21
+ 1. Read the template from `flydocs/knowledge/templates/<category>.md`
22
+ 2. Gather content from the user — ask clarifying questions if context is thin
23
+ 3. Fill in the frontmatter:
24
+ - `title`: Descriptive, specific title
25
+ - `created`: Today's date (`YYYY-MM-DD`)
26
+ - `lastUpdated`: Today's date
27
+ - `relatedIssues`: Any issue IDs discussed in this session
28
+ - Category-specific fields (see template)
29
+ 4. Write the doc to the correct directory:
30
+ - Decisions: `flydocs/knowledge/decisions/NNN-title.md` (auto-increment NNN)
31
+ - Features: `flydocs/knowledge/features/feature-name.md`
32
+ - Notes: `flydocs/knowledge/notes/topic-name.md`
33
+ - Product: `flydocs/knowledge/product/document-name.md`
34
+ 5. Update `flydocs/knowledge/INDEX.md`:
35
+ - Add entry to the appropriate table
36
+ - Update the Quick Reference count
37
+ - Use a concise description (helps agents assess relevance without loading the full doc)
38
+
39
+ ### 3. For Existing Docs
40
+
41
+ 1. Find the doc in INDEX.md or by searching `flydocs/knowledge/`
42
+ 2. Read the current content
43
+ 3. Apply the update
44
+ 4. Update the `lastUpdated` field in frontmatter
45
+ 5. Update the INDEX.md entry if the description changed
46
+
47
+ ### 4. Confirm
48
+
49
+ Report what was created or updated:
50
+
51
+ - File path
52
+ - Category and title
53
+ - INDEX.md entry added/updated
54
+
55
+ ## Triggers
56
+
57
+ - "document this" / "knowledge" / "add to knowledge base"
58
+ - "create an ADR" / "record this decision" / "capture this learning"
59
+ - "add a note about" / "document this discovery"
60
+
61
+ $ARGUMENTS
@@ -13,7 +13,6 @@ triggers:
13
13
  - update description
14
14
  - update issue
15
15
  - project update
16
- - Linear
17
16
  - cloud
18
17
  ---
19
18
 
@@ -29,49 +28,63 @@ All scripts: `python3 .claude/skills/flydocs-cloud/scripts/<script>`
29
28
 
30
29
  ### Shared Contract Scripts
31
30
 
32
- | Script | Usage | Output |
33
- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | `create_issue.py` | `--title "..." --type feature [--description "..."] [--description-file PATH] [--priority 0-4] [--estimate 1-5] [--assignee STR] [--labels "a,b"] [--triage] \| stdin` | `{id, identifier, title, url}` |
35
- | `transition.py` | `<ref> <STATUS> "<comment>"` | `{success, issue, previousStatus, newStatus}` |
36
- | `comment.py` | `<ref> ["<comment>"] \| stdin` | `{success, commentId}` |
37
- | `list_issues.py` | `[--status STATUS[,STATUS]] [--active] [--project ID] [--milestone ID] [--assignee STR] [--mine] [--limit N]` | `[{id, identifier, title, status, assignee, priority, dueDate, milestone, milestoneId, milestoneSortOrder, project, projectId}]` |
38
- | `get_issue.py` | `<ref> [--fields basic\|full]` | `{id, identifier, title, description, status, assignee, priority, estimate, dueDate, milestone, milestoneId, project, projectId, comments[]}` |
39
- | `assign.py` | `<ref> <assignee>` | `{success, issue, assignee}` |
40
- | `update_description.py` | `<ref> --text "..." \| --file PATH \| stdin` | `{success, issue}` |
31
+ | Script | Usage | Output |
32
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
33
+ | `create_issue.py` | `--title "..." --type feature [--description "..."] [--description-file PATH] [--priority N] [--estimate N] [--assignee STR] [--project ID] [--labels "a,b"] [--milestone ID_OR_NAME] [--triage] \| stdin` | `{id, identifier, title, url}` |
34
+ | `transition.py` | `<ref> <STATUS> "<comment>"` | `{success, issue, previousStatus, newStatus}` |
35
+ | `comment.py` | `<ref> ["<comment>"] \| stdin` | `{success, commentId}` |
36
+ | `list_issues.py` | `[--status STATUS[,STATUS]] [--active] [--project ID] [--milestone ID] [--assignee STR] [--mine] [--limit N]` | `[{id, identifier, title, status, assignee, priority, dueDate, milestone, milestoneId, milestoneSortOrder, project, projectId}]` |
37
+ | `get_issue.py` | `<ref> [--fields basic\|full]` | `{id, identifier, title, description, status, assignee, priority, estimate, dueDate, milestone, milestoneId, project, projectId, comments[]}` |
38
+ | `assign.py` | `<ref> <assignee> \| --unassign` | `{success, issue, assignee}` |
39
+ | `update_description.py` | `<ref> --text "..." \| --file PATH \| stdin` | `{success, issue}` |
41
40
 
42
41
  ### Extended Scripts
43
42
 
44
- | Script | Usage | Output |
45
- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ |
46
- | `update_issue.py` | `<ref> [--title "..."] [--priority 0-4] [--estimate 1-5] [--assignee STR] [--state STATUS] [--description "..."] [--description-file PATH] [--labels "a,b"] [--comment "..."]` | `{success, issue, updated[]}` |
47
- | `estimate.py` | `<ref> <1-5>` | `{success, issue, estimate}` |
48
- | `priority.py` | `<ref> <0-4>` | `{success, issue, priority}` |
49
- | `link.py` | `<ref> <related_ref> <type>` | `{success, type}` |
50
- | `project_update.py` | `--health STATUS --body "..." [--body-file PATH]` | `{success, id}` |
51
- | `list_projects.py` | `[--active] [--all]` | `[{id, name, state}]` — `--all` bypasses product scope |
52
- | `create_project.py` | `--name "..." [--description "..."]` | `{id, name, url}` |
53
- | `assign_cycle.py` | `<ref> [cycle_id]` | `{success, issue, cycle}` |
54
- | `list_cycles.py` | `[--active]` | `[{id, name, number, startsAt, endsAt}]` |
55
- | `list_milestones.py` | `[--all]` | `[{id, name, targetDate}]` |
56
- | `create_milestone.py` | `--name "..." [--project ID] [--target-date DATE]` | `{id, name}` |
57
- | `assign_milestone.py` | `<ref> <milestone_id>` | `{success, issue, milestone}` |
43
+ | Script | Usage | Output |
44
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
45
+ | `update_issue.py` | `<ref> [--title "..."] [--priority N] [--estimate N] [--assignee STR] [--state STATUS] [--description "..."] [--description-file PATH] [--labels "a,b"] [--milestone ID_OR_NAME] [--comment "..."]` | `{success, issue, updated[]}` |
46
+ | `estimate.py` | `<ref> <points>` | `{success, issue, estimate}` — relay validates against provider scale |
47
+ | `priority.py` | `<ref> <0-4>` | `{success, issue, priority}` |
48
+ | `link.py` | `<ref> <related_ref> <type>` | `{success, type}` |
49
+ | `project_update.py` | `--health STATUS --body "..." [--body-file PATH]` | `{success, id}` |
50
+ | `list_projects.py` | `[--active] [--all]` | `[{id, name, state}]` — `--all` bypasses product scope |
51
+ | `create_project.py` | `--name "..." [--description "..."]` | `{id, name, url}` |
52
+ | `assign_cycle.py` | `<ref> [cycle_id]` | `{success, issue, cycle}` |
53
+ | `list_cycles.py` | `[--active]` | `[{id, name, number, startsAt, endsAt}]` |
54
+ | `list_milestones.py` | `[--all]` | `[{id, name, targetDate}]` |
55
+ | `create_milestone.py` | `--name "..." [--project ID] [--target-date DATE]` | `{id, name}` |
56
+ | `update_milestone.py` | `<milestone_id> [--name "..."] [--target-date DATE] [--description "..."]` | `{success, id, name}` |
57
+ | `delete_milestone.py` | `<milestone_id>` | `{success, id}` |
58
+ | `assign_milestone.py` | `<ref> <milestone_id>` | `{success, issue, milestone}` |
58
59
 
59
60
  ### Workspace Scripts
60
61
 
61
- | Script | Usage | Output |
62
- | ---------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
63
- | `list_teams.py` | (no args) | `[{id, name, key}]` |
64
- | `set_team.py` | `<team_id>` | `{success}` — updates relay preference and local config `provider.teamId` |
65
- | `list_labels.py` | (no args) | `[{id, name, color}]` — requires team to be set first |
66
- | `set_labels.py` | `--defaults '["a"]' --type-map '{"feature":["F"],...}' \| stdin` | `{success, validated, defaults, typeMap}` — stores label config on relay |
62
+ | Script | Usage | Output |
63
+ | ----------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
64
+ | `list_providers.py` | (no args) | `[{type, name, connected}]` |
65
+ | `set_provider.py` | `<provider_type>` (`linear` or `jira`) | `{success}` — updates relay routing and local config `provider.type` |
66
+ | `list_teams.py` | (no args) | `[{id, name, key}]` — returns Linear teams or Jira projects (relay normalizes) |
67
+ | `create_team.py` | `--name "..." [--key KEY] [--description "..."] [--parent <team_id>]` | `{id, name, key}` — `--parent` is Linear-only (sub-teams) |
68
+ | `set_team.py` | `<team_id>` | `{success}` — updates relay preference and local config; for Jira, sets the active Jira project |
69
+ | `list_labels.py` | (no args) | `[{id, name, color}]` — requires team to be set first |
70
+ | `set_labels.py` | `--defaults '["a"]' --type-map '{"feature":["F"],...}' \| stdin` | `{success, validated, defaults, typeMap}` — stores label config on relay |
71
+ | `list_statuses.py` | (no args) | `{states, currentMapping, flydocsStatuses}` — provider workflow states and current mapping |
72
+ | `set_status_mapping.py` | `--auto \| --mapping '{"BACKLOG":"Backlog",...}' \| stdin` | `{success, mapping, matched, total}` — stores status mapping on relay |
73
+ | `set_identity.py` | `<provider> <provider-user-id>` | `{success, provider, providerId}` — binds provider user ID for `--mine` resolution |
74
+ | `set_preferences.py` | `[--workspace ID] [--assignee self\|ID] [--display JSON]` | `{success, preferences}` — no flags = GET current; with flags = POST update |
75
+ | `get_estimate_scale.py` | (no args) | `{scale, type}` — provider's valid estimate values (fixed or freeform) |
76
+ | `refresh_labels.py` | `[--fix]` | `{valid, stale, details}` — validates config label IDs against relay; `--fix` updates stale IDs |
77
+ | `validate_setup.py` | (no args) | `{valid, checks, missing[]}` — validates workspace config, caches result, sets setupComplete |
67
78
 
68
79
  ### Script Notes
69
80
 
70
- - **How it works**: Scripts call the FlyDocs Relay REST API, which translates to the provider (Linear, Jira) server-side. Same interface and output as before — the transport changed from direct GraphQL to managed REST.
81
+ - **How it works**: Scripts call the FlyDocs Relay REST API, which translates to the provider (Linear, Jira) server-side. Same interface and output as before — the transport changed from direct GraphQL to managed REST. For Jira, the relay handles Markdown-to-ADF (Atlassian Document Format) conversion automatically.
71
82
  - **`list_issues.py --active`**: Returns all non-terminal issues (excludes Done, Archived, Canceled, Duplicate).
72
83
  - **`list_issues.py --status`**: Accepts comma-separated statuses: `--status READY,IMPLEMENTING,BLOCKED`
73
84
  - **`get_issue.py --fields basic`**: Skips comment fetch for faster responses.
74
85
  - **`update_issue.py`**: Bulk update — sets multiple fields in a single API call. Prefer over separate scripts when updating more than one field.
86
+ - **Estimate validation**: The relay validates estimates server-side against the provider's scale. Use `get_estimate_scale.py` to discover valid values before setting. Linear uses a fixed scale `[0, 1, 2, 3, 5, 8, 13, 21]`; Jira accepts freeform values.
87
+ - **Label staleness**: Label IDs are team-scoped and can go stale when switching teams or projects. Use `refresh_labels.py` to validate config label IDs, and `refresh_labels.py --fix` to auto-update stale ones.
75
88
  - **Shell-safe text input**: For text with special characters, pipe via stdin with a single-quoted heredoc:
76
89
  ```bash
77
90
  python3 update_description.py ENG-123 <<'EOF'
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Assign an issue to a user via the FlyDocs Relay API."""
2
+ """Assign or unassign an issue via the FlyDocs Relay API."""
3
3
 
4
4
  import sys
5
5
  from pathlib import Path
@@ -7,10 +7,16 @@ from pathlib import Path
7
7
  sys.path.insert(0, str(Path(__file__).parent))
8
8
  from flydocs_api import get_client, output_json, fail
9
9
 
10
- if len(sys.argv) < 3:
11
- fail("Usage: assign.py <ref> <assignee>")
10
+ if len(sys.argv) < 2:
11
+ fail("Usage: assign.py <ref> <assignee> | assign.py <ref> --unassign")
12
12
 
13
- ref, assignee = sys.argv[1], sys.argv[2]
13
+ ref = sys.argv[1]
14
+ unassign = "--unassign" in sys.argv
15
+
16
+ if not unassign and len(sys.argv) < 3:
17
+ fail("Usage: assign.py <ref> <assignee> | assign.py <ref> --unassign")
18
+
19
+ assignee = None if unassign else sys.argv[2]
14
20
  client = get_client()
15
21
 
16
22
  result = client.post(f"/issues/{ref}/assign", {"assignee": assignee})
@@ -15,10 +15,12 @@ def main():
15
15
  parser.add_argument("--type", required=True, choices=["feature", "bug", "chore", "idea"], dest="issue_type")
16
16
  parser.add_argument("--description", default="")
17
17
  parser.add_argument("--description-file", default="", dest="description_file")
18
- parser.add_argument("--priority", type=int, default=3, choices=range(5))
19
- parser.add_argument("--estimate", type=int, default=0, choices=[0, 1, 2, 3, 5])
18
+ parser.add_argument("--priority", type=int, default=3, help="Priority (0-4, relay translates per provider)")
19
+ parser.add_argument("--estimate", type=int, default=0, help="Estimate points (relay translates per provider)")
20
20
  parser.add_argument("--assignee", default=None)
21
+ parser.add_argument("--project", default=None, help="Project ID")
21
22
  parser.add_argument("--labels", default=None, help="Comma-separated ad-hoc label names")
23
+ parser.add_argument("--milestone", default=None, help="Milestone ID or name (resolved by name lookup)")
22
24
  parser.add_argument("--triage", action="store_true")
23
25
  args = parser.parse_args()
24
26
 
@@ -43,12 +45,30 @@ def main():
43
45
  body["estimate"] = args.estimate
44
46
  if args.assignee:
45
47
  body["assignee"] = args.assignee
48
+ if args.project:
49
+ body["projectId"] = args.project
46
50
  if args.labels:
47
51
  body["labels"] = [l.strip() for l in args.labels.split(",") if l.strip()]
48
52
  if args.triage:
49
53
  body["triage"] = True
50
54
 
51
55
  client = get_client()
56
+
57
+ if args.milestone:
58
+ milestone_id = args.milestone
59
+ # If it doesn't look like a UUID, resolve by name
60
+ if len(milestone_id) != 36 or "-" not in milestone_id:
61
+ milestones = client.get("/milestones")
62
+ match = None
63
+ for m in milestones:
64
+ if m["name"].lower() == milestone_id.lower():
65
+ match = m
66
+ break
67
+ if not match:
68
+ fail(f"Milestone not found: {milestone_id}")
69
+ milestone_id = match["id"]
70
+ body["milestoneId"] = milestone_id
71
+
52
72
  result = client.post("/issues", body)
53
73
 
54
74
  output_json({
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ """Create a team/project via the FlyDocs Relay API."""
3
+
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ sys.path.insert(0, str(Path(__file__).parent))
9
+ from flydocs_api import get_client, output_json
10
+
11
+
12
+ def main():
13
+ parser = argparse.ArgumentParser(description="Create team or project")
14
+ parser.add_argument("--name", required=True)
15
+ parser.add_argument("--key", default=None, help="Team key (e.g., PROD). Auto-generated if omitted.")
16
+ parser.add_argument("--description", default=None)
17
+ parser.add_argument("--parent", default=None, dest="parent_id", help="Parent team ID for sub-team (Linear only, ignored for Jira)")
18
+ args = parser.parse_args()
19
+
20
+ body: dict = {"name": args.name}
21
+ if args.key:
22
+ body["key"] = args.key
23
+ if args.description:
24
+ body["description"] = args.description
25
+ if args.parent_id:
26
+ body["parentId"] = args.parent_id
27
+
28
+ client = get_client()
29
+ result = client.post("/teams", body)
30
+
31
+ output_json({
32
+ "id": result["id"],
33
+ "name": result["name"],
34
+ "key": result.get("key", ""),
35
+ })
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()