@flydocs/cli 0.6.0-alpha.4 → 0.6.0-alpha.6

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 (38) hide show
  1. package/dist/cli.js +12 -16
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +11 -9
  4. package/template/.claude/commands/flydocs-setup.md +34 -19
  5. package/template/.claude/commands/knowledge.md +61 -0
  6. package/template/.claude/skills/flydocs-cloud/SKILL.md +43 -36
  7. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +19 -2
  8. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +3 -3
  9. package/template/.claude/skills/flydocs-cloud/scripts/delete_milestone.py +21 -0
  10. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +9 -5
  11. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +4 -0
  12. package/template/.claude/skills/flydocs-cloud/scripts/get_estimate_scale.py +23 -0
  13. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +1 -1
  14. package/template/.claude/skills/flydocs-cloud/scripts/refresh_labels.py +87 -0
  15. package/template/.claude/skills/flydocs-cloud/scripts/set_identity.py +38 -0
  16. package/template/.claude/skills/flydocs-cloud/scripts/set_preferences.py +49 -0
  17. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +5 -4
  18. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +22 -4
  19. package/template/.claude/skills/flydocs-cloud/scripts/update_milestone.py +42 -0
  20. package/template/.claude/skills/flydocs-workflow/SKILL.md +23 -18
  21. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  22. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +105 -0
  23. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  24. package/template/.claude/skills/flydocs-workflow/session.md +24 -16
  25. package/template/.claude/skills/flydocs-workflow/stages/capture.md +8 -3
  26. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  27. package/template/.claude/skills/flydocs-workflow/stages/implement.md +28 -4
  28. package/template/.claude/skills/flydocs-workflow/stages/refine.md +20 -4
  29. package/template/.claude/skills/flydocs-workflow/stages/review.md +14 -2
  30. package/template/.flydocs/config.json +2 -2
  31. package/template/.flydocs/version +1 -1
  32. package/template/AGENTS.md +8 -8
  33. package/template/flydocs/knowledge/INDEX.md +38 -53
  34. package/template/flydocs/knowledge/README.md +60 -9
  35. package/template/flydocs/knowledge/templates/decision.md +47 -0
  36. package/template/flydocs/knowledge/templates/feature.md +35 -0
  37. package/template/flydocs/knowledge/templates/note.md +25 -0
  38. package/template/manifest.json +8 -2
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ var CLI_VERSION, CLI_NAME, PACKAGE_NAME, POSTHOG_API_KEY;
15
15
  var init_constants = __esm({
16
16
  "src/lib/constants.ts"() {
17
17
  "use strict";
18
- CLI_VERSION = "0.6.0-alpha.4";
18
+ CLI_VERSION = "0.6.0-alpha.6";
19
19
  CLI_NAME = "flydocs";
20
20
  PACKAGE_NAME = "@flydocs/cli";
21
21
  POSTHOG_API_KEY = "phc_v1MSJTQDFkMS90CBh3mxIz3v8bYCCnKU6v1ir6bz0Xn";
@@ -2046,7 +2046,7 @@ var init_install = __esm({
2046
2046
  {
2047
2047
  value: "cloud",
2048
2048
  label: "Cloud (managed)",
2049
- hint: "Sync with Linear \u2014 run flydocs connect after install"
2049
+ hint: "Sync with Linear, Jira, and more"
2050
2050
  }
2051
2051
  ]
2052
2052
  });
@@ -3519,7 +3519,7 @@ var init_connect = __esm({
3519
3519
  },
3520
3520
  key: {
3521
3521
  type: "string",
3522
- description: "API key (fdk_ for relay, lin_api_ for direct Linear)"
3522
+ description: "FlyDocs API key (fdk_...)"
3523
3523
  }
3524
3524
  },
3525
3525
  async run({ args }) {
@@ -3548,22 +3548,19 @@ var init_connect = __esm({
3548
3548
  console.log(` ${pc11.bold("Connect to FlyDocs Cloud")}`);
3549
3549
  console.log();
3550
3550
  console.log(
3551
- ` ${pc11.dim("FlyDocs API key (fdk_...): Get from your FlyDocs dashboard")}`
3552
- );
3553
- console.log(
3554
- ` ${pc11.dim("Linear API key (lin_api_...): Get from Linear \u2192 Settings \u2192 API")}`
3551
+ ` ${pc11.dim("Get your API key (fdk_...) from your FlyDocs dashboard")}`
3555
3552
  );
3556
3553
  console.log();
3557
3554
  let apiKey = args.key ?? "";
3558
3555
  if (!apiKey) {
3559
3556
  const keyInput = await text3({
3560
3557
  message: "Enter your API key",
3561
- placeholder: "fdk_... or lin_api_...",
3558
+ placeholder: "fdk_...",
3562
3559
  validate(value) {
3563
3560
  if (!value.trim()) return "API key is required";
3564
3561
  const type = detectKeyType(value.trim());
3565
3562
  if (type === "unknown")
3566
- return "Key must start with fdk_ (FlyDocs) or lin_api_ (Linear)";
3563
+ return "Key must start with fdk_ (FlyDocs API key)";
3567
3564
  return void 0;
3568
3565
  }
3569
3566
  });
@@ -3575,7 +3572,9 @@ var init_connect = __esm({
3575
3572
  }
3576
3573
  const keyType = detectKeyType(apiKey);
3577
3574
  if (keyType === "unknown") {
3578
- printError("Unrecognized key format. Expected fdk_ or lin_api_ prefix.");
3575
+ printError(
3576
+ "Unrecognized key format. Expected fdk_ prefix (FlyDocs API key)."
3577
+ );
3579
3578
  process.exit(1);
3580
3579
  }
3581
3580
  printInfo("Validating API key...");
@@ -3617,8 +3616,7 @@ var init_connect = __esm({
3617
3616
  }
3618
3617
  const wasLocal = config.tier === "local";
3619
3618
  config.tier = "cloud";
3620
- config.provider = config.provider ?? { type: "linear", teamId: null };
3621
- config.provider.type = "linear";
3619
+ config.provider = config.provider ?? { type: null, teamId: null };
3622
3620
  await writeConfig(targetDir, config);
3623
3621
  printStatus("Config updated to cloud tier");
3624
3622
  if (wasLocal) {
@@ -3706,7 +3704,7 @@ var init_upgrade = __esm({
3706
3704
  );
3707
3705
  console.log();
3708
3706
  console.log(
3709
- ` Your issues sync with Linear via the cloud mechanism skill.`
3707
+ ` Your issues sync with your provider via the cloud mechanism skill.`
3710
3708
  );
3711
3709
  console.log(
3712
3710
  ` Run ${pc12.cyan("flydocs connect")} to update your connection settings.`
@@ -3719,9 +3717,7 @@ var init_upgrade = __esm({
3719
3717
  console.log(` You're currently on the ${pc12.yellow("local")} tier.`);
3720
3718
  console.log(` Upgrade to cloud for:`);
3721
3719
  console.log();
3722
- console.log(
3723
- ` ${pc12.cyan("\u2192")} Issue sync with Linear (Jira coming soon)`
3724
- );
3720
+ console.log(` ${pc12.cyan("\u2192")} Issue sync with Linear, Jira, and more`);
3725
3721
  console.log(` ${pc12.cyan("\u2192")} Project milestones and cycle management`);
3726
3722
  console.log(` ${pc12.cyan("\u2192")} Team assignment and priority tracking`);
3727
3723
  console.log(` ${pc12.cyan("\u2192")} Project health updates and dashboards`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flydocs/cli",
3
- "version": "0.6.0-alpha.4",
3
+ "version": "0.6.0-alpha.6",
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 -->
@@ -326,7 +326,10 @@ python3 .claude/skills/flydocs-cloud/scripts/set_provider.py <provider_type>
326
326
  This stores the provider type on the relay (for routing) and updates
327
327
  `provider.type` in local config.
328
328
 
329
- **Step 3: Select or create team.**
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.
330
333
 
331
334
  If `provider.teamId` in config is null, discover available teams:
332
335
 
@@ -335,20 +338,24 @@ python3 .claude/skills/flydocs-cloud/scripts/list_teams.py
335
338
  ```
336
339
 
337
340
  Present the teams to the user as a numbered list showing name and key.
338
- Add a final option: **"Create a new team"**. If only one team exists,
341
+ Add a final option: **"Create a new team/project"**. If only one team exists,
339
342
  confirm it.
340
343
 
341
- If the user selects **"Create a new team"**:
344
+ If the user selects **"Create a new team/project"**:
342
345
 
343
346
  1. Ask for team name (required) and key (optional — auto-generated if omitted)
344
- 2. Ask if this should be a sub-team under an existing team (show the list
345
- again for parent selection, or "None — top-level team")
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.
346
351
  3. Create via:
347
352
 
348
353
  ```bash
349
354
  python3 .claude/skills/flydocs-cloud/scripts/create_team.py --name "Team Name" [--key KEY] [--parent <parent_team_id>]
350
355
  ```
351
356
 
357
+ > The `--parent` flag is Linear-only (sub-teams). Omit for Jira.
358
+
352
359
  After selection or creation, store the preference on the relay and in local
353
360
  config:
354
361
 
@@ -358,8 +365,12 @@ python3 .claude/skills/flydocs-cloud/scripts/set_team.py <team_id>
358
365
 
359
366
  This stores the team preference server-side (the relay uses it for all
360
367
  team-scoped operations) and updates `provider.teamId` in local config.
368
+ For Jira, this sets the active Jira project.
369
+
370
+ **Step 4: Get or create project (Linear only).**
361
371
 
362
- **Step 4: Get or create project.**
372
+ > **Jira provider:** Skip this step. Jira organizes issues directly within
373
+ > projects (selected in Step 3). There is no secondary project container.
363
374
 
364
375
  Query existing projects:
365
376
 
@@ -421,24 +432,24 @@ The relay auto-maps by case-insensitive name matching (e.g., "In Progress"
421
432
  maps to `IMPLEMENTING`, "Done" maps to `COMPLETE`, "Backlog" maps to
422
433
  `BACKLOG`).
423
434
 
424
- **Review the result:** The response includes which FlyDocs statuses were
425
- mapped and which were not. Show the user:
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:
426
438
 
427
439
  ```
428
- Status mapping:
440
+ Status mapping (matched 6/10):
429
441
  BACKLOG -> Backlog
430
442
  READY -> Ready
431
443
  IMPLEMENTING -> In Progress
432
444
  REVIEW -> In Review
433
445
  COMPLETE -> Done
434
446
  CANCELED -> Canceled
435
- (unmapped) -> BLOCKED, TESTING
436
447
  ```
437
448
 
438
- If some statuses are unmapped, **warn but don't block** — transitions for
439
- unmapped statuses will fall back to provider name matching. If the user
440
- wants to fix unmapped statuses, fetch available provider states and let
441
- them map manually:
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:
442
453
 
443
454
  ```bash
444
455
  python3 .claude/skills/flydocs-cloud/scripts/list_statuses.py
@@ -476,6 +487,7 @@ Update `.flydocs/config.json`:
476
487
  ## Phase 3: Milestones — Cloud Only
477
488
 
478
489
  > **Local tier:** Skip this phase entirely.
490
+ > **Jira provider:** Skip this phase. Milestone support for Jira is planned for a future release.
479
491
  > **FlyDocs Update:** Skip if milestones already exist.
480
492
 
481
493
  **Step 1: Check existing milestones.**
@@ -536,7 +548,9 @@ For each work item, follow the capture procedure from
536
548
  `.claude/skills/flydocs-workflow/stages/capture.md`:
537
549
 
538
550
  - Determine type (feature, bug, chore, idea)
539
- - Create via the mechanism script (cloud: pass `--project` from Phase 2):
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):
540
554
  ```bash
541
555
  python3 .claude/skills/flydocs-{tier}/scripts/create_issue.py \
542
556
  --title "Issue title" --type feature --priority 3 --estimate 2 \
@@ -544,9 +558,10 @@ For each work item, follow the capture procedure from
544
558
  ```
545
559
  - For quick ideas, use `--triage` flag
546
560
 
547
- **Step 3: Milestone assignment (cloud only).**
561
+ **Step 3: Milestone assignment (Linear only).**
548
562
 
549
563
  > **Local tier:** Skip this step.
564
+ > **Jira provider:** Skip this step.
550
565
 
551
566
  After creating issues, assign them to milestones:
552
567
 
@@ -697,8 +712,8 @@ Wrapping up:
697
712
 
698
713
  **For cloud tier:**
699
714
 
700
- - Linear project URL (if available from create_project response)
701
- - 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
702
717
 
703
718
  **For local tier:**
704
719
 
@@ -756,7 +771,7 @@ Throughout the setup flow:
756
771
  error message from stderr and ask the user how to proceed. Do not retry
757
772
  silently.
758
773
  - **Missing API key** — For cloud tier, cannot proceed past Phase 2 without
759
- `LINEAR_API_KEY`. Guide the user to set it up.
774
+ `FLYDOCS_API_KEY`. Guide the user to set it up.
760
775
  - **Missing config** — If `.flydocs/config.json` doesn't exist, the user
761
776
  likely needs to run `flydocs` (install) first. Advise them accordingly.
762
777
  - **Partial completion** — If the user needs to stop mid-setup, note which
@@ -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,54 +28,62 @@ 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] [--project ID] [--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>` | `{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_providers.py` | (no args) | `[{type, name, connected}]` |
64
- | `set_provider.py` | `<provider_type>` (`linear` or `jira`) | `{success}` — updates relay routing and local config `provider.type` |
65
- | `list_teams.py` | (no args) | `[{id, name, key}]` |
66
- | `create_team.py` | `--name "..." [--key KEY] [--description "..."] [--parent <team_id>]` | `{id, name, key}` — `--parent` creates a sub-team |
67
- | `set_team.py` | `<team_id>` | `{success}` — updates relay preference and local config `provider.teamId` |
68
- | `list_labels.py` | (no args) | `[{id, name, color}]` — requires team to be set first |
69
- | `set_labels.py` | `--defaults '["a"]' --type-map '{"feature":["F"],...}' \| stdin` | `{success, validated, defaults, typeMap}` — stores label config on relay |
70
- | `list_statuses.py` | (no args) | `[{name, type, color}]` — provider workflow states for manual mapping |
71
- | `set_status_mapping.py` | `--auto \| --mapping '{"BACKLOG":"Backlog",...}' \| stdin` | `{success, mapping, unmapped[]}` — stores status mapping 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 |
72
77
 
73
78
  ### Script Notes
74
79
 
75
- - **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.
80
+ - **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.
76
81
  - **`list_issues.py --active`**: Returns all non-terminal issues (excludes Done, Archived, Canceled, Duplicate).
77
82
  - **`list_issues.py --status`**: Accepts comma-separated statuses: `--status READY,IMPLEMENTING,BLOCKED`
78
83
  - **`get_issue.py --fields basic`**: Skips comment fetch for faster responses.
79
84
  - **`update_issue.py`**: Bulk update — sets multiple fields in a single API call. Prefer over separate scripts when updating more than one field.
85
+ - **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.
86
+ - **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.
80
87
  - **Shell-safe text input**: For text with special characters, pipe via stdin with a single-quoted heredoc:
81
88
  ```bash
82
89
  python3 update_description.py ENG-123 <<'EOF'
@@ -15,11 +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
21
  parser.add_argument("--project", default=None, help="Project ID")
22
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)")
23
24
  parser.add_argument("--triage", action="store_true")
24
25
  args = parser.parse_args()
25
26
 
@@ -52,6 +53,22 @@ def main():
52
53
  body["triage"] = True
53
54
 
54
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
+
55
72
  result = client.post("/issues", body)
56
73
 
57
74
  output_json({
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Create a team (or sub-team) via the FlyDocs Relay API."""
2
+ """Create a team/project via the FlyDocs Relay API."""
3
3
 
4
4
  import argparse
5
5
  import sys
@@ -10,11 +10,11 @@ from flydocs_api import get_client, output_json
10
10
 
11
11
 
12
12
  def main():
13
- parser = argparse.ArgumentParser(description="Create team")
13
+ parser = argparse.ArgumentParser(description="Create team or project")
14
14
  parser.add_argument("--name", required=True)
15
15
  parser.add_argument("--key", default=None, help="Team key (e.g., PROD). Auto-generated if omitted.")
16
16
  parser.add_argument("--description", default=None)
17
- parser.add_argument("--parent", default=None, dest="parent_id", help="Parent team ID to create a sub-team")
17
+ parser.add_argument("--parent", default=None, dest="parent_id", help="Parent team ID for sub-team (Linear only, ignored for Jira)")
18
18
  args = parser.parse_args()
19
19
 
20
20
  body: dict = {"name": args.name}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python3
2
+ """Delete a milestone via the FlyDocs Relay API."""
3
+
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ sys.path.insert(0, str(Path(__file__).parent))
8
+ from flydocs_api import get_client, output_json, fail
9
+
10
+ if len(sys.argv) < 2:
11
+ fail("Usage: delete_milestone.py <milestone_id>")
12
+
13
+ milestone_id = sys.argv[1]
14
+
15
+ client = get_client()
16
+ result = client.delete(f"/milestones/{milestone_id}")
17
+
18
+ output_json({
19
+ "success": result.get("success", True),
20
+ "id": result.get("id", milestone_id),
21
+ })
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- """Set estimate on an issue via the FlyDocs Relay API."""
2
+ """Set estimate on an issue via the FlyDocs Relay API.
3
+
4
+ The relay validates the estimate against the provider's scale server-side.
5
+ Use get_estimate_scale.py to discover valid values before setting.
6
+ """
3
7
 
4
8
  import sys
5
9
  from pathlib import Path
@@ -8,16 +12,16 @@ sys.path.insert(0, str(Path(__file__).parent))
8
12
  from flydocs_api import get_client, output_json, fail
9
13
 
10
14
  if len(sys.argv) < 3:
11
- fail("Usage: estimate.py <ref> <1-5>")
15
+ fail("Usage: estimate.py <ref> <points>")
12
16
 
13
17
  ref = sys.argv[1]
14
18
  try:
15
19
  estimate = int(sys.argv[2])
16
20
  except ValueError:
17
- fail("Estimate must be a number (1-5)")
21
+ fail("Estimate must be a number")
18
22
 
19
- if estimate not in (1, 2, 3, 5):
20
- fail("Estimate must be 1 (XS), 2 (S), 3 (M), or 5 (L)")
23
+ if estimate < 0:
24
+ fail("Estimate must be a non-negative integer")
21
25
 
22
26
  client = get_client()
23
27
  result = client.put(f"/issues/{ref}/estimate", {"estimate": estimate})
@@ -160,6 +160,10 @@ class FlyDocsClient:
160
160
  """PATCH request to relay API."""
161
161
  return self._request("PATCH", path, body=body)
162
162
 
163
+ def delete(self, path: str) -> dict:
164
+ """DELETE request to relay API."""
165
+ return self._request("DELETE", path)
166
+
163
167
  def _log_operation(self, method: str, path: str, status: int, result: dict | list):
164
168
  """Log operation metadata to local log file."""
165
169
  try:
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ """Get the provider's estimate scale via the FlyDocs Relay API.
3
+
4
+ Returns the valid estimate values for the connected provider.
5
+ Linear: fixed scale [0, 1, 2, 3, 5, 8, 13, 21]
6
+ Jira: freeform (any positive number)
7
+ """
8
+
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ sys.path.insert(0, str(Path(__file__).parent))
13
+ from flydocs_api import get_client, output_json
14
+
15
+
16
+ def main():
17
+ client = get_client()
18
+ result = client.get("/auth/estimates")
19
+ output_json(result)
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """List available teams via the FlyDocs Relay API."""
2
+ """List available teams/projects via the FlyDocs Relay API."""
3
3
 
4
4
  import sys
5
5
  from pathlib import Path