@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.
- package/dist/cli.js +2054 -470
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +43 -48
- package/template/.claude/agents/implementation-agent.md +1 -1
- package/template/.claude/agents/pm-agent.md +1 -1
- package/template/.claude/commands/activate.md +1 -1
- package/template/.claude/commands/attach.md +1 -1
- package/template/.claude/commands/block.md +2 -2
- package/template/.claude/commands/capture.md +1 -1
- package/template/.claude/commands/close.md +1 -1
- package/template/.claude/commands/flydocs-setup.md +359 -72
- package/template/.claude/commands/flydocs-upgrade.md +26 -27
- package/template/.claude/commands/implement.md +1 -1
- package/template/.claude/commands/knowledge.md +61 -0
- package/template/.claude/commands/new-project.md +1 -1
- package/template/.claude/commands/onboard.md +275 -0
- package/template/.claude/commands/project-update.md +1 -1
- package/template/.claude/commands/refine.md +1 -1
- package/template/.claude/commands/review.md +1 -1
- package/template/.claude/commands/start-session.md +1 -1
- package/template/.claude/commands/status.md +1 -1
- package/template/.claude/commands/validate.md +1 -1
- package/template/.claude/commands/wrap-session.md +1 -1
- package/template/.claude/hooks/auto-approve.py +212 -0
- package/template/.claude/hooks/post-pr-check.py +108 -0
- package/template/.claude/hooks/post-transition-check.py +281 -0
- package/template/.claude/hooks/prompt-submit.py +554 -0
- package/template/.claude/hooks/session-start.py +262 -0
- package/template/.claude/hooks/stop-gate.py +162 -0
- package/template/.claude/settings.json +41 -4
- package/template/.claude/skills/README.md +23 -25
- package/template/.claude/skills/flydocs-workflow/SKILL.md +134 -42
- package/template/.claude/skills/flydocs-workflow/cursor-rule.mdc +9 -8
- package/template/.claude/skills/flydocs-workflow/reference/comment-templates.md +1 -0
- package/template/.claude/skills/flydocs-workflow/reference/golden-rules.md +28 -17
- package/template/.claude/skills/flydocs-workflow/reference/graph-schema.md +116 -0
- package/template/.claude/skills/flydocs-workflow/reference/pr-workflow.md +120 -0
- package/template/.claude/skills/flydocs-workflow/reference/priority-estimates.md +37 -15
- package/template/.claude/skills/flydocs-workflow/reference/service-descriptor-schema.md +260 -0
- package/template/.claude/skills/flydocs-workflow/reference/status-workflow.md +26 -26
- package/template/.claude/skills/flydocs-workflow/scripts/_local/__init__.py +0 -0
- package/template/.claude/skills/{flydocs-local/scripts/flydocs_api.py → flydocs-workflow/scripts/_local/file_store.py} +137 -47
- package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +724 -0
- package/template/{.flydocs → .claude/skills/flydocs-workflow}/scripts/generate_manifest.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_build.py +132 -1
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_query.py +18 -5
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_session.py +1 -10
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_update.py +4 -4
- package/template/.claude/skills/{flydocs-context-graph → flydocs-workflow}/scripts/graph_utils.py +2 -1
- package/template/.claude/skills/flydocs-workflow/scripts/issues.py +738 -0
- package/template/.claude/skills/flydocs-workflow/scripts/projects.py +144 -0
- package/template/.claude/skills/flydocs-workflow/scripts/pull_services.py +128 -0
- package/template/.claude/skills/flydocs-workflow/scripts/push_service.py +132 -0
- package/template/.claude/skills/flydocs-workflow/scripts/session.py +54 -0
- package/template/.claude/skills/flydocs-workflow/scripts/test_enforcement.py +225 -0
- package/template/.claude/skills/flydocs-workflow/scripts/workspace.py +902 -0
- package/template/.claude/skills/flydocs-workflow/session.md +87 -29
- package/template/.claude/skills/flydocs-workflow/stages/activate.md +18 -7
- package/template/.claude/skills/flydocs-workflow/stages/capture.md +10 -5
- package/template/.claude/skills/flydocs-workflow/stages/close.md +4 -3
- package/template/.claude/skills/flydocs-workflow/stages/implement.md +33 -9
- package/template/.claude/skills/flydocs-workflow/stages/refine.md +22 -6
- package/template/.claude/skills/flydocs-workflow/stages/review.md +16 -4
- package/template/.claude/skills/flydocs-workflow/stages/validate.md +3 -1
- package/template/.claude/skills/flydocs-workflow/templates/pr/default.md +33 -0
- package/template/.cursor/agents/implementation-agent.md +1 -1
- package/template/.cursor/agents/pm-agent.md +2 -2
- package/template/.cursor/hooks.json +10 -3
- package/template/.env.example +6 -6
- package/template/.flydocs/config.json +5 -18
- package/template/.flydocs/templates/README.md +13 -14
- package/template/.flydocs/templates/bug.md +17 -153
- package/template/.flydocs/templates/chore.md +10 -98
- package/template/.flydocs/templates/feature.md +12 -158
- package/template/.flydocs/templates/idea.md +11 -111
- package/template/.flydocs/templates/quick-capture.md +4 -8
- package/template/.flydocs/version +1 -1
- package/template/AGENTS.md +44 -32
- package/template/CHANGELOG.md +37 -0
- package/template/flydocs/README.md +1 -3
- package/template/flydocs/context/project.md +6 -3
- package/template/flydocs/design-system/README.md +3 -3
- package/template/flydocs/knowledge/INDEX.md +38 -53
- package/template/flydocs/knowledge/README.md +60 -9
- package/template/flydocs/knowledge/templates/decision.md +47 -0
- package/template/flydocs/knowledge/templates/feature.md +35 -0
- package/template/flydocs/knowledge/templates/note.md +25 -0
- package/template/manifest.json +24 -20
- package/template/.claude/skills/flydocs-cloud/SKILL.md +0 -113
- package/template/.claude/skills/flydocs-cloud/cursor-rule.mdc +0 -50
- package/template/.claude/skills/flydocs-cloud/scripts/assign.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/assign_cycle.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/assign_milestone.py +0 -22
- package/template/.claude/skills/flydocs-cloud/scripts/comment.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/create_issue.py +0 -66
- package/template/.claude/skills/flydocs-cloud/scripts/create_milestone.py +0 -35
- package/template/.claude/skills/flydocs-cloud/scripts/create_project.py +0 -33
- package/template/.claude/skills/flydocs-cloud/scripts/create_team.py +0 -39
- package/template/.claude/skills/flydocs-cloud/scripts/estimate.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/flydocs_api.py +0 -210
- package/template/.claude/skills/flydocs-cloud/scripts/get_issue.py +0 -24
- package/template/.claude/skills/flydocs-cloud/scripts/link.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_cycles.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_issues.py +0 -44
- package/template/.claude/skills/flydocs-cloud/scripts/list_labels.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/list_milestones.py +0 -28
- package/template/.claude/skills/flydocs-cloud/scripts/list_projects.py +0 -31
- package/template/.claude/skills/flydocs-cloud/scripts/list_providers.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/list_teams.py +0 -19
- package/template/.claude/skills/flydocs-cloud/scripts/priority.py +0 -29
- package/template/.claude/skills/flydocs-cloud/scripts/project_update.py +0 -45
- package/template/.claude/skills/flydocs-cloud/scripts/set_labels.py +0 -68
- package/template/.claude/skills/flydocs-cloud/scripts/set_provider.py +0 -46
- package/template/.claude/skills/flydocs-cloud/scripts/set_team.py +0 -41
- package/template/.claude/skills/flydocs-cloud/scripts/transition.py +0 -26
- package/template/.claude/skills/flydocs-cloud/scripts/update_description.py +0 -36
- package/template/.claude/skills/flydocs-cloud/scripts/update_issue.py +0 -82
- package/template/.claude/skills/flydocs-context-graph/SKILL.md +0 -87
- package/template/.claude/skills/flydocs-context-graph/schema.md +0 -78
- package/template/.claude/skills/flydocs-context-graph/scripts/graph_context.py +0 -338
- package/template/.claude/skills/flydocs-context7/SKILL.md +0 -105
- package/template/.claude/skills/flydocs-context7/cursor-rule.mdc +0 -49
- package/template/.claude/skills/flydocs-context7/scripts/context7.py +0 -293
- package/template/.claude/skills/flydocs-estimates/SKILL.md +0 -384
- package/template/.claude/skills/flydocs-figma/SKILL.md +0 -377
- package/template/.claude/skills/flydocs-figma/references/PROMPTING.md +0 -108
- package/template/.claude/skills/flydocs-figma/references/TROUBLESHOOTING.md +0 -112
- package/template/.claude/skills/flydocs-local/SKILL.md +0 -103
- package/template/.claude/skills/flydocs-local/cursor-rule.mdc +0 -43
- package/template/.claude/skills/flydocs-local/scripts/assign.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/comment.py +0 -27
- package/template/.claude/skills/flydocs-local/scripts/create_issue.py +0 -44
- package/template/.claude/skills/flydocs-local/scripts/estimate.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/get_issue.py +0 -20
- package/template/.claude/skills/flydocs-local/scripts/link.py +0 -41
- package/template/.claude/skills/flydocs-local/scripts/list_issues.py +0 -34
- package/template/.claude/skills/flydocs-local/scripts/priority.py +0 -37
- package/template/.claude/skills/flydocs-local/scripts/project_update.py +0 -67
- package/template/.claude/skills/flydocs-local/scripts/status_summary.py +0 -16
- package/template/.claude/skills/flydocs-local/scripts/transition.py +0 -24
- package/template/.claude/skills/flydocs-local/scripts/update_description.py +0 -35
- package/template/.claude/skills/flydocs-local/scripts/update_issue.py +0 -84
- package/template/.flydocs/hooks/auto-approve.py +0 -71
- package/template/.flydocs/hooks/prompt-submit.py +0 -277
- package/template/.flydocs/scripts/skill_manager.py +0 -541
- /package/template/{.flydocs → .claude}/hooks/post-edit.py +0 -0
- /package/template/.claude/skills/{flydocs-estimates/references → flydocs-workflow/reference}/provider-costs.md +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{bug.md → issues/bug.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{chore.md → issues/chore.md} +0 -0
- /package/template/.claude/skills/flydocs-workflow/templates/{feature.md → issues/feature.md} +0 -0
- /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()
|