feat: local issue cache and draft-then-push workflow
Replace fetch_issue.py with four scripts around a flat, greppable cache in tmp/issues/. Planning stays offline and issues reach Gitea in one push: - issue_get.py: fetch by key or by filter (--milestone/--label/-q). The list endpoint carries issue bodies, so a whole milestone costs one request per 50 issues. Gitea silently ignores an unresolvable milestones= filter and returns the entire backlog, so the milestone is resolved up front and every returned issue is re-checked locally. --deps walks the dependency graph downwards via the structured sections plus native dependencies and writes tree-<slug>.md. - issue_push.py: validate a local draft against the canonical format, create missing labels with the right colors and exclusivity, POST, delete the draft. - issue_list.py: discovery to stdout, writes nothing. - issue_index.py: rebuild INDEX.md from what is on disk. Files use one metadata field per line with inline lists so plain grep works without a parser. This is a cache and a drafting area, not a mirror: no drift tracking, no sync back. Projects are not fetchable — the projects API is 404 on Gitea 1.26; documented alongside the milestone caveat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
issue_list.py — discovery: which issue numbers exist, one line each.
|
||||
|
||||
Prints to stdout and writes nothing: INDEX.md is a map of the local cache, and
|
||||
this command deliberately does not pollute it. Use it to pick numbers, then
|
||||
fetch them with issue_get.py.
|
||||
|
||||
#42 open type/task, tech/sql Wire sqlc into the repo layer
|
||||
|
||||
Usage:
|
||||
issue_list.py [--state open|closed|all] [--label L]… [-q TEXT]
|
||||
[--milestone M] [--limit N] [--page N] [--repo owner/repo]
|
||||
|
||||
Login: the operator's pin from .claude/settings.local.json (see /tea:auth).
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _tea import list_issues, repo_base, require_login # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="List Gitea issues (stdout only, no files)")
|
||||
ap.add_argument("--state", default="open", choices=["open", "closed", "all"])
|
||||
ap.add_argument("--label", action="append", default=[],
|
||||
help="filter by label; repeat for AND")
|
||||
ap.add_argument("-q", "--query", help="search text in title/body")
|
||||
ap.add_argument("--milestone", help="milestone id or title")
|
||||
ap.add_argument("--limit", type=int, default=30)
|
||||
ap.add_argument("--repo", help="owner/repo (default: auto-detect from CWD git remote)")
|
||||
args = ap.parse_args()
|
||||
|
||||
login = require_login()
|
||||
got, ms_title = list_issues(login, repo_base(args.repo), state=args.state,
|
||||
labels=args.label, query=args.query,
|
||||
milestone=args.milestone, limit=args.limit)
|
||||
for iss in got:
|
||||
labels = ", ".join(l.get("name", "") for l in iss.get("labels") or []) or "-"
|
||||
print("#%-5d %-7s %-38s %s" % (iss["number"], iss.get("state", ""),
|
||||
labels[:38], iss.get("title", "")))
|
||||
scope = " in milestone %s" % ms_title if ms_title else ""
|
||||
print("%d issue(s)%s — fetch them with: issue_get.py %s"
|
||||
% (len(got), scope,
|
||||
("--milestone %s" % args.milestone) if args.milestone else "<n>"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user