@flydocs/cli 0.6.0-alpha.3 → 0.6.0-alpha.30

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 (151) hide show
  1. package/dist/cli.js +2054 -470
  2. package/package.json +1 -1
  3. package/template/.claude/CLAUDE.md +43 -48
  4. package/template/.claude/agents/implementation-agent.md +1 -1
  5. package/template/.claude/agents/pm-agent.md +1 -1
  6. package/template/.claude/commands/activate.md +1 -1
  7. package/template/.claude/commands/attach.md +1 -1
  8. package/template/.claude/commands/block.md +2 -2
  9. package/template/.claude/commands/capture.md +1 -1
  10. package/template/.claude/commands/close.md +1 -1
  11. package/template/.claude/commands/flydocs-setup.md +359 -72
  12. package/template/.claude/commands/flydocs-upgrade.md +26 -27
  13. package/template/.claude/commands/implement.md +1 -1
  14. package/template/.claude/commands/knowledge.md +61 -0
  15. package/template/.claude/commands/new-project.md +1 -1
  16. package/template/.claude/commands/onboard.md +275 -0
  17. package/template/.claude/commands/project-update.md +1 -1
  18. package/template/.claude/commands/refine.md +1 -1
  19. package/template/.claude/commands/review.md +1 -1
  20. package/template/.claude/commands/start-session.md +1 -1
  21. package/template/.claude/commands/status.md +1 -1
  22. package/template/.claude/commands/validate.md +1 -1
  23. package/template/.claude/commands/wrap-session.md +1 -1
  24. package/template/.claude/hooks/auto-approve.py +212 -0
  25. package/template/.claude/hooks/post-pr-check.py +108 -0
  26. package/template/.claude/hooks/post-transition-check.py +281 -0
  27. package/template/.claude/hooks/prompt-submit.py +554 -0
  28. package/template/.claude/hooks/session-start.py +262 -0
  29. package/template/.claude/hooks/stop-gate.py +162 -0
  30. package/template/.claude/settings.json +41 -4
  31. package/template/.claude/skills/README.md +23 -25
  32. package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
  33. package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
  34. package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
  35. package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
  36. package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
  37. package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
  38. package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
  39. package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +260 -0
  40. package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
  41. package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
  42. package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
  43. package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +724 -0
  44. package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
  45. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
  46. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
  47. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
  48. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
  49. package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
  50. package/template/.claude/skills/flydocs-workflow/scripts/issues.py +738 -0
  51. package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
  52. package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
  53. package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
  54. package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
  55. package/template/.claude/skills/flydocs-workflow/scripts/test_enforcement.py +225 -0
  56. package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +902 -0
  57. package/template/.claude/skills/flydocs-workflow/session.md +87 -29
  58. package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
  59. package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
  60. package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
  61. package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
  62. package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
  63. package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
  64. package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
  65. package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
  66. package/template/.cursor/agents/implementation-agent.md +1 -1
  67. package/template/.cursor/agents/pm-agent.md +2 -2
  68. package/template/.cursor/hooks.json +10 -3
  69. package/template/.env.example +6 -6
  70. package/template/.flydocs/config.json +5 -18
  71. package/template/.flydocs/templates/README.md +13 -14
  72. package/template/.flydocs/templates/bug.md +17 -153
  73. package/template/.flydocs/templates/chore.md +10 -98
  74. package/template/.flydocs/templates/feature.md +12 -158
  75. package/template/.flydocs/templates/idea.md +11 -111
  76. package/template/.flydocs/templates/quick-capture.md +4 -8
  77. package/template/.flydocs/version +1 -1
  78. package/template/AGENTS.md +44 -32
  79. package/template/CHANGELOG.md +37 -0
  80. package/template/flydocs/README.md +1 -3
  81. package/template/flydocs/context/project.md +6 -3
  82. package/template/flydocs/design-system/README.md +3 -3
  83. package/template/flydocs/knowledge/INDEX.md +38 -53
  84. package/template/flydocs/knowledge/README.md +60 -9
  85. package/template/flydocs/knowledge/templates/decision.md +47 -0
  86. package/template/flydocs/knowledge/templates/feature.md +35 -0
  87. package/template/flydocs/knowledge/templates/note.md +25 -0
  88. package/template/manifest.json +24 -20
  89. package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -113
  90. package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
  91. package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
  92. package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
  93. package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
  94. package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
  95. package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -66
  96. package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
  97. package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
  98. package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
  99. package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
  100. package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
  101. package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
  102. package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
  103. package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
  104. package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
  105. package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
  106. package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
  107. package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
  108. package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +0 -19
  109. package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
  110. package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
  111. package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
  112. package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
  113. package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +0 -46
  114. package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
  115. package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
  116. package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
  117. package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
  118. package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
  119. package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
  120. package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
  121. package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
  122. package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
  123. package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
  124. package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
  125. package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
  126. package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
  127. package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
  128. package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
  129. package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
  130. package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
  131. package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
  132. package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
  133. package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
  134. package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
  135. package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
  136. package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
  137. package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
  138. package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
  139. package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
  140. package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
  141. package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
  142. package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
  143. package/template/.flydocs/hooks/auto-approve.py +0 -71
  144. package/template/.flydocs/hooks/prompt-submit.py +0 -277
  145. package/template/.flydocs/scripts/skill_manager.py +0 -541
  146. /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
  147. /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
  148. /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
  149. /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
  150. /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
  151. /package/template/.claude/skills/flydocs-workflow/templates/{idea.md → issues/idea.md} +0 -0
@@ -1,43 +0,0 @@
1
- ---
2
- description: File-based issue management — local mechanism for FlyDocs
3
- alwaysApply: true
4
- ---
5
-
6
- <!-- Condensed from SKILL.md — update both when changing patterns -->
7
-
8
- # FlyDocs Local Mechanism
9
-
10
- Issues stored as markdown files with YAML frontmatter. No accounts, no API keys, no network.
11
-
12
- ## Script Contract
13
-
14
- All scripts: `python3 .claude/skills/flydocs-local/scripts/<script>`
15
-
16
- | Script | Key Arguments |
17
- |--------|---------------|
18
- | `create_issue.py` | `--title "..." --type feature [--priority 0-4] [--estimate 1-5] [--assignee STR] [--triage]` |
19
- | `transition.py` | `<ref> <STATUS> "<comment>"` |
20
- | `comment.py` | `<ref> "<comment>"` |
21
- | `list_issues.py` | `[--status STATUS] [--mine] [--limit N]` |
22
- | `get_issue.py` | `<ref>` |
23
- | `assign.py` | `<ref> <assignee>` |
24
- | `update_description.py` | `<ref> --text "..." \| --file PATH` |
25
- | `status_summary.py` | `[--project ID]` |
26
-
27
- ## Status Values
28
-
29
- `BACKLOG`, `READY`, `IMPLEMENTING`, `BLOCKED`, `REVIEW`, `TESTING`, `COMPLETE`, `ARCHIVED`, `CANCELED`, `DUPLICATE`
30
-
31
- ## Storage Layout
32
-
33
- ```
34
- flydocs/issues/
35
- backlog/ implementing/ review/
36
- ready/ testing/ done/
37
- ```
38
-
39
- Issues: `FD-XXX-slug.md` with YAML frontmatter. IDs auto-increment via `.flydocs/issues/.counter`.
40
-
41
- ## Error Handling
42
-
43
- Exit 0 = success (JSON on stdout). Exit 1 = error (message on stderr).
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Assign an issue to a person."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import assign_issue
10
-
11
- if len(sys.argv) < 3:
12
- print("Usage: assign.py <ref> <assignee>", file=sys.stderr)
13
- sys.exit(1)
14
-
15
- try:
16
- result = assign_issue(sys.argv[1], sys.argv[2])
17
- print(json.dumps(result))
18
- except Exception as e:
19
- print(str(e), file=sys.stderr)
20
- sys.exit(1)
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Add a comment to an issue."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import add_comment
10
-
11
- if len(sys.argv) < 2:
12
- print("Usage: comment.py <ref> [<comment>] (or pipe via stdin)", file=sys.stderr)
13
- sys.exit(1)
14
-
15
- try:
16
- ref = sys.argv[1]
17
- body = sys.argv[2] if len(sys.argv) > 2 else ""
18
- if not body and not sys.stdin.isatty():
19
- body = sys.stdin.read().strip()
20
- if not body:
21
- print("Provide comment as argument or pipe via stdin", file=sys.stderr)
22
- sys.exit(1)
23
- result = add_comment(ref, body)
24
- print(json.dumps(result))
25
- except Exception as e:
26
- print(str(e), file=sys.stderr)
27
- sys.exit(1)
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Create a new local issue."""
3
-
4
- import argparse
5
- import json
6
- import sys
7
- from pathlib import Path
8
-
9
- sys.path.insert(0, str(Path(__file__).parent))
10
- from flydocs_api import create_issue, ISSUE_TYPES
11
-
12
- parser = argparse.ArgumentParser(description="Create a new issue")
13
- parser.add_argument("--title", required=True)
14
- parser.add_argument("--type", required=True, choices=sorted(ISSUE_TYPES), dest="issue_type")
15
- parser.add_argument("--description", default="")
16
- parser.add_argument("--description-file", default="", dest="description_file")
17
- parser.add_argument("--priority", type=int, default=3, choices=range(5))
18
- parser.add_argument("--estimate", type=int, default=0, choices=range(6))
19
- parser.add_argument("--assignee", default="")
20
- parser.add_argument("--triage", action="store_true")
21
-
22
- try:
23
- args = parser.parse_args()
24
-
25
- # Resolve description: --description-file > stdin > --description
26
- description = args.description
27
- if args.description_file:
28
- description = Path(args.description_file).read_text()
29
- elif not description and not sys.stdin.isatty():
30
- description = sys.stdin.read().strip()
31
-
32
- result = create_issue(
33
- title=args.title,
34
- issue_type=args.issue_type,
35
- description=description,
36
- priority=args.priority,
37
- estimate=args.estimate,
38
- assignee=args.assignee,
39
- triage=args.triage,
40
- )
41
- print(json.dumps(result))
42
- except Exception as e:
43
- print(str(e), file=sys.stderr)
44
- sys.exit(1)
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set estimate on an issue."""
3
-
4
- import json
5
- import sys
6
- from datetime import datetime
7
- from pathlib import Path
8
-
9
- sys.path.insert(0, str(Path(__file__).parent))
10
- from flydocs_api import _find_issue, _parse_issue, _write_issue
11
-
12
- if len(sys.argv) < 3:
13
- print("Usage: estimate.py <ref> <1-5>", file=sys.stderr)
14
- sys.exit(1)
15
-
16
- ref = sys.argv[1]
17
- try:
18
- estimate = int(sys.argv[2])
19
- if estimate < 1 or estimate > 5:
20
- raise ValueError
21
- except ValueError:
22
- print("Estimate must be a number (1-5)", file=sys.stderr)
23
- sys.exit(1)
24
-
25
- try:
26
- filepath = _find_issue(ref)
27
- data = _parse_issue(filepath)
28
- data["estimate"] = estimate
29
- data["updated"] = datetime.now().strftime("%Y-%m-%d")
30
-
31
- fm = {k: v for k, v in data.items() if k not in ("description", "comments", "_path")}
32
- _write_issue(filepath, fm, data["description"], data["comments"])
33
-
34
- print(json.dumps({"success": True, "issue": ref, "estimate": estimate}))
35
- except Exception as e:
36
- print(str(e), file=sys.stderr)
37
- sys.exit(1)
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Get full details for an issue."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import get_issue
10
-
11
- if len(sys.argv) < 2:
12
- print("Usage: get_issue.py <ref>", file=sys.stderr)
13
- sys.exit(1)
14
-
15
- try:
16
- result = get_issue(sys.argv[1])
17
- print(json.dumps(result))
18
- except Exception as e:
19
- print(str(e), file=sys.stderr)
20
- sys.exit(1)
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Create a relationship between two issues."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import _find_issue, add_comment
10
-
11
- LINK_TYPES = {"blocks", "related", "duplicate"}
12
-
13
- if len(sys.argv) < 4:
14
- print(f"Usage: link.py <ref> <related_ref> <type>\nTypes: {', '.join(sorted(LINK_TYPES))}", file=sys.stderr)
15
- sys.exit(1)
16
-
17
- ref, related_ref, link_type = sys.argv[1], sys.argv[2], sys.argv[3].lower()
18
- if link_type not in LINK_TYPES:
19
- print(f"Invalid type: {link_type}. Valid: {', '.join(sorted(LINK_TYPES))}", file=sys.stderr)
20
- sys.exit(1)
21
-
22
- try:
23
- # Verify both issues exist
24
- _find_issue(ref)
25
- _find_issue(related_ref)
26
-
27
- # Record the link as comments on both issues
28
- if link_type == "blocks":
29
- add_comment(ref, f"**Link**: blocks {related_ref}")
30
- add_comment(related_ref, f"**Link**: blocked by {ref}")
31
- elif link_type == "duplicate":
32
- add_comment(ref, f"**Link**: duplicate of {related_ref}")
33
- add_comment(related_ref, f"**Link**: duplicate of {ref}")
34
- else:
35
- add_comment(ref, f"**Link**: related to {related_ref}")
36
- add_comment(related_ref, f"**Link**: related to {ref}")
37
-
38
- print(json.dumps({"success": True, "type": link_type}))
39
- except Exception as e:
40
- print(str(e), file=sys.stderr)
41
- sys.exit(1)
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env python3
2
- """List issues with optional filters."""
3
-
4
- import argparse
5
- import json
6
- import os
7
- import sys
8
- from pathlib import Path
9
-
10
- sys.path.insert(0, str(Path(__file__).parent))
11
- from flydocs_api import list_issues
12
-
13
- TERMINAL_STATUSES = {"COMPLETE", "ARCHIVED", "CANCELED", "DUPLICATE"}
14
-
15
- parser = argparse.ArgumentParser(description="List issues")
16
- parser.add_argument("--status", default="")
17
- parser.add_argument("--active", action="store_true",
18
- help="All non-terminal states (excludes Done, Archived, Canceled, Duplicate)")
19
- parser.add_argument("--assignee", default="")
20
- parser.add_argument("--mine", action="store_true")
21
- parser.add_argument("--limit", type=int, default=50)
22
-
23
- try:
24
- args = parser.parse_args()
25
- assignee = args.assignee
26
- if args.mine:
27
- assignee = os.environ.get("USER", os.environ.get("USERNAME", ""))
28
- result = list_issues(status=args.status.upper() if args.status else "", assignee=assignee, limit=args.limit)
29
- if args.active:
30
- result = [r for r in result if r.get("status") not in TERMINAL_STATUSES]
31
- print(json.dumps(result))
32
- except Exception as e:
33
- print(str(e), file=sys.stderr)
34
- sys.exit(1)
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Set priority on an issue."""
3
-
4
- import json
5
- import sys
6
- from datetime import datetime
7
- from pathlib import Path
8
-
9
- sys.path.insert(0, str(Path(__file__).parent))
10
- from flydocs_api import _find_issue, _parse_issue, _write_issue
11
-
12
- if len(sys.argv) < 3:
13
- print("Usage: priority.py <ref> <0-4>", file=sys.stderr)
14
- sys.exit(1)
15
-
16
- ref = sys.argv[1]
17
- try:
18
- priority = int(sys.argv[2])
19
- if priority < 0 or priority > 4:
20
- raise ValueError
21
- except ValueError:
22
- print("Priority must be a number (0-4)", file=sys.stderr)
23
- sys.exit(1)
24
-
25
- try:
26
- filepath = _find_issue(ref)
27
- data = _parse_issue(filepath)
28
- data["priority"] = priority
29
- data["updated"] = datetime.now().strftime("%Y-%m-%d")
30
-
31
- fm = {k: v for k, v in data.items() if k not in ("description", "comments", "_path")}
32
- _write_issue(filepath, fm, data["description"], data["comments"])
33
-
34
- print(json.dumps({"success": True, "issue": ref, "priority": priority}))
35
- except Exception as e:
36
- print(str(e), file=sys.stderr)
37
- sys.exit(1)
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Post a project health update (local: saves to flydocs/updates/)."""
3
-
4
- import argparse
5
- import json
6
- import sys
7
- from datetime import datetime
8
- from pathlib import Path
9
-
10
- HEALTH_VALUES = {"onTrack", "atRisk", "offTrack"}
11
-
12
- parser = argparse.ArgumentParser(description="Post project update")
13
- parser.add_argument("--health", required=True, choices=sorted(HEALTH_VALUES))
14
- parser.add_argument("--body", default="")
15
- parser.add_argument("--body-file", default="")
16
- parser.add_argument("--project", default=None)
17
-
18
- try:
19
- args = parser.parse_args()
20
-
21
- body = args.body
22
- if args.body_file:
23
- p = Path(args.body_file)
24
- if not p.exists():
25
- print(f"File not found: {args.body_file}", file=sys.stderr)
26
- sys.exit(1)
27
- body = p.read_text().strip()
28
- if not body and not sys.stdin.isatty():
29
- body = sys.stdin.read().strip()
30
- if not body:
31
- print("Provide --body, --body-file, or pipe via stdin", file=sys.stderr)
32
- sys.exit(1)
33
-
34
- # Find project root
35
- cwd = Path.cwd()
36
- root = None
37
- for parent in [cwd, *cwd.parents]:
38
- if (parent / ".flydocs" / "config.json").exists():
39
- root = parent
40
- break
41
- if not root:
42
- print("Not in a FlyDocs project", file=sys.stderr)
43
- sys.exit(1)
44
-
45
- # Save update to flydocs/updates/
46
- updates_dir = root / "flydocs" / "updates"
47
- updates_dir.mkdir(parents=True, exist_ok=True)
48
-
49
- now = datetime.now()
50
- update_id = now.strftime("%Y%m%d-%H%M%S")
51
- filename = f"{update_id}.md"
52
-
53
- content = f"""---
54
- health: {args.health}
55
- date: {now.strftime("%Y-%m-%d")}
56
- time: {now.strftime("%H:%M")}
57
- ---
58
-
59
- {body}
60
- """
61
-
62
- (updates_dir / filename).write_text(content)
63
-
64
- print(json.dumps({"success": True, "id": update_id}))
65
- except Exception as e:
66
- print(str(e), file=sys.stderr)
67
- sys.exit(1)
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Show project status summary (issue counts by status)."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import status_summary
10
-
11
- try:
12
- result = status_summary()
13
- print(json.dumps(result))
14
- except Exception as e:
15
- print(str(e), file=sys.stderr)
16
- sys.exit(1)
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Transition an issue to a new status."""
3
-
4
- import json
5
- import sys
6
- from pathlib import Path
7
-
8
- sys.path.insert(0, str(Path(__file__).parent))
9
- from flydocs_api import transition, STATUSES
10
-
11
- if len(sys.argv) < 4:
12
- print("Usage: transition.py <ref> <STATUS> <comment>", file=sys.stderr)
13
- sys.exit(1)
14
-
15
- try:
16
- ref, status, comment = sys.argv[1], sys.argv[2].upper(), sys.argv[3]
17
- if status not in STATUSES:
18
- print(f"Invalid status: {status}. Valid: {', '.join(sorted(STATUSES.keys()))}", file=sys.stderr)
19
- sys.exit(1)
20
- result = transition(ref, status, comment)
21
- print(json.dumps(result))
22
- except Exception as e:
23
- print(str(e), file=sys.stderr)
24
- sys.exit(1)
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Update an issue's description."""
3
-
4
- import argparse
5
- import json
6
- import sys
7
- from pathlib import Path
8
-
9
- sys.path.insert(0, str(Path(__file__).parent))
10
- from flydocs_api import update_description
11
-
12
- parser = argparse.ArgumentParser(description="Update issue description")
13
- parser.add_argument("ref")
14
- parser.add_argument("--text", default="")
15
- parser.add_argument("--file", default="", dest="filepath")
16
-
17
- try:
18
- args = parser.parse_args()
19
- text = args.text
20
- if args.filepath:
21
- try:
22
- text = Path(args.filepath).read_text()
23
- except FileNotFoundError:
24
- print(f"File not found: {args.filepath}", file=sys.stderr)
25
- sys.exit(1)
26
- if not text and not sys.stdin.isatty():
27
- text = sys.stdin.read().strip()
28
- if not text:
29
- print("Provide --text, --file, or pipe via stdin", file=sys.stderr)
30
- sys.exit(1)
31
- result = update_description(args.ref, text)
32
- print(json.dumps(result))
33
- except Exception as e:
34
- print(str(e), file=sys.stderr)
35
- sys.exit(1)
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Bulk update an issue — set multiple fields in a single operation."""
3
-
4
- import argparse
5
- import json
6
- import sys
7
- from datetime import datetime
8
- from pathlib import Path
9
-
10
- sys.path.insert(0, str(Path(__file__).parent))
11
- from flydocs_api import _find_issue, _parse_issue, _write_issue, add_comment
12
- from flydocs_api import transition as do_transition
13
-
14
- parser = argparse.ArgumentParser(description="Update issue fields")
15
- parser.add_argument("ref", help="Issue reference (e.g., FD-001)")
16
- parser.add_argument("--title", default=None)
17
- parser.add_argument("--priority", type=int, choices=range(5), default=None)
18
- parser.add_argument("--estimate", type=int, choices=range(1, 6), default=None)
19
- parser.add_argument("--assignee", default=None)
20
- parser.add_argument("--state", default=None)
21
- parser.add_argument("--description", default=None)
22
- parser.add_argument("--description-file", default=None)
23
- parser.add_argument("--comment", default=None)
24
-
25
- try:
26
- args = parser.parse_args()
27
-
28
- updated_fields = []
29
-
30
- # Handle state transition first (moves the file between directories)
31
- if args.state:
32
- do_transition(args.ref, args.state.upper(), f"Status changed to {args.state.upper()}")
33
- updated_fields.append("state")
34
-
35
- # Handle comment next (appends to file via add_comment)
36
- if args.comment:
37
- add_comment(args.ref, args.comment)
38
- updated_fields.append("comment")
39
-
40
- # Now read the current state of the file (after any transition/comment writes)
41
- filepath = _find_issue(args.ref)
42
- data = _parse_issue(filepath)
43
-
44
- if args.title is not None:
45
- data["title"] = args.title
46
- updated_fields.append("title")
47
-
48
- if args.priority is not None:
49
- data["priority"] = args.priority
50
- updated_fields.append("priority")
51
-
52
- if args.estimate is not None:
53
- data["estimate"] = args.estimate
54
- updated_fields.append("estimate")
55
-
56
- if args.assignee is not None:
57
- data["assignee"] = args.assignee
58
- updated_fields.append("assignee")
59
-
60
- description = data.get("description", "")
61
- if args.description_file is not None:
62
- p = Path(args.description_file)
63
- if not p.exists():
64
- print(f"File not found: {args.description_file}", file=sys.stderr)
65
- sys.exit(1)
66
- description = p.read_text()
67
- updated_fields.append("description")
68
- elif args.description is not None:
69
- description = args.description
70
- updated_fields.append("description")
71
-
72
- if not updated_fields:
73
- print("No fields to update. Use --title, --priority, --estimate, --assignee, --state, --description, or --comment", file=sys.stderr)
74
- sys.exit(1)
75
-
76
- # Write updated issue
77
- data["updated"] = datetime.now().strftime("%Y-%m-%d")
78
- fm = {k: v for k, v in data.items() if k not in ("description", "comments", "_path")}
79
- _write_issue(filepath, fm, description, data["comments"])
80
-
81
- print(json.dumps({"success": True, "issue": args.ref, "updated": updated_fields}))
82
- except Exception as e:
83
- print(str(e), file=sys.stderr)
84
- sys.exit(1)
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- PreToolUse Hook: Auto-approve FlyDocs mechanism and workflow scripts
4
-
5
- Auto-approves Bash commands that execute scripts in any FlyDocs skill:
6
- - .claude/skills/flydocs-local/scripts/
7
- - .claude/skills/flydocs-cloud/scripts/
8
- - .claude/skills/flydocs-workflow/scripts/
9
-
10
- Exit codes:
11
- - 0 with JSON: Approved (decision in output)
12
- - 0 with no output: No opinion, continue normally
13
- - 2: Block (stderr shown to AI)
14
- """
15
-
16
- import sys
17
- import json
18
- import re
19
-
20
-
21
- # Pattern matches any flydocs skill scripts directory
22
- # Covers: flydocs-local, flydocs-cloud, flydocs-workflow, flydocs-context-graph
23
- APPROVED_PATTERN = re.compile(
24
- r'python3?\s+(?:["\']?(?:\$CLAUDE_PROJECT_DIR|\$\{CLAUDE_PROJECT_DIR\}|\.)["\']?/)?\.?claude/skills/flydocs-(?:local|cloud|workflow|context-graph|context7)/scripts/\w+\.py'
25
- )
26
-
27
-
28
- def should_approve(command: str) -> bool:
29
- """Check if command executes a FlyDocs skill script."""
30
- return bool(APPROVED_PATTERN.search(command))
31
-
32
-
33
- def get_script_info(command: str) -> tuple[str, str]:
34
- """Extract skill name and script name from command."""
35
- match = re.search(
36
- r'\.claude/skills/(flydocs-\w+)/scripts/(\w+\.py)', command
37
- )
38
- if match:
39
- return match.group(1), match.group(2)
40
- return "unknown", "unknown"
41
-
42
-
43
- def main():
44
- try:
45
- input_data = json.load(sys.stdin)
46
- except (json.JSONDecodeError, EOFError):
47
- sys.exit(0)
48
-
49
- tool_name = input_data.get('tool_name', '')
50
- tool_input = input_data.get('tool_input', {})
51
-
52
- if tool_name != 'Bash':
53
- sys.exit(0)
54
-
55
- command = tool_input.get('command', '')
56
-
57
- if should_approve(command):
58
- skill, script = get_script_info(command)
59
- result = {
60
- "decision": "approve",
61
- "reason": f"Auto-approved FlyDocs script: {skill}/{script}"
62
- }
63
- print(json.dumps(result))
64
- sys.exit(0)
65
- else:
66
- # No opinion — exit 0 with no output to avoid hook error
67
- sys.exit(0)
68
-
69
-
70
- if __name__ == "__main__":
71
- main()