gfly 0.1.0__tar.gz

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 (44) hide show
  1. gfly-0.1.0/.editorconfig +18 -0
  2. gfly-0.1.0/.github/CODEOWNERS +7 -0
  3. gfly-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +61 -0
  4. gfly-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. gfly-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
  6. gfly-0.1.0/.github/PUBLISH_CHECKLIST.md +47 -0
  7. gfly-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  8. gfly-0.1.0/.github/workflows/ci.yml +25 -0
  9. gfly-0.1.0/.github/workflows/release.yml +53 -0
  10. gfly-0.1.0/.gitignore +17 -0
  11. gfly-0.1.0/AGENTS.md +41 -0
  12. gfly-0.1.0/CHANGELOG.md +28 -0
  13. gfly-0.1.0/CITATION.cff +22 -0
  14. gfly-0.1.0/CODE_OF_CONDUCT.md +99 -0
  15. gfly-0.1.0/CONTRIBUTING.md +47 -0
  16. gfly-0.1.0/LICENSE-APACHE +202 -0
  17. gfly-0.1.0/LICENSE-MIT +21 -0
  18. gfly-0.1.0/PKG-INFO +230 -0
  19. gfly-0.1.0/README.md +207 -0
  20. gfly-0.1.0/SECURITY.md +46 -0
  21. gfly-0.1.0/SUPPORT.md +23 -0
  22. gfly-0.1.0/demo/gfly.gif +0 -0
  23. gfly-0.1.0/demo/gfly.tape +45 -0
  24. gfly-0.1.0/llms.txt +30 -0
  25. gfly-0.1.0/pyproject.toml +40 -0
  26. gfly-0.1.0/site/.gitignore +1 -0
  27. gfly-0.1.0/site/gfly.gif +0 -0
  28. gfly-0.1.0/site/index.html +244 -0
  29. gfly-0.1.0/spec.md +230 -0
  30. gfly-0.1.0/src/gfly/SKILL.md +74 -0
  31. gfly-0.1.0/src/gfly/__init__.py +19 -0
  32. gfly-0.1.0/src/gfly/__main__.py +6 -0
  33. gfly-0.1.0/src/gfly/airports.py +45 -0
  34. gfly-0.1.0/src/gfly/auth.py +134 -0
  35. gfly-0.1.0/src/gfly/backend.py +321 -0
  36. gfly-0.1.0/src/gfly/cli.py +629 -0
  37. gfly-0.1.0/src/gfly/errors.py +127 -0
  38. gfly-0.1.0/src/gfly/output.py +135 -0
  39. gfly-0.1.0/src/gfly/skill.py +9 -0
  40. gfly-0.1.0/src/gfly/throttle.py +144 -0
  41. gfly-0.1.0/tests/conftest.py +80 -0
  42. gfly-0.1.0/tests/test_cli.py +228 -0
  43. gfly-0.1.0/tests/test_schema_snapshot.py +42 -0
  44. gfly-0.1.0/uv.lock +554 -0
@@ -0,0 +1,18 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.{md,yml,yaml,json,toml,tape}]
12
+ indent_size = 2
13
+
14
+ [*.md]
15
+ trim_trailing_whitespace = false
16
+
17
+ [Makefile]
18
+ indent_style = tab
@@ -0,0 +1,7 @@
1
+ # Default owner for everything in this repo.
2
+ * @rnwolfe
3
+
4
+ # The agent-CLI contract surface — review with extra care.
5
+ /src/gfly/output.py @rnwolfe
6
+ /src/gfly/errors.py @rnwolfe
7
+ /tests/test_schema_snapshot.py @rnwolfe
@@ -0,0 +1,61 @@
1
+ name: Bug report
2
+ description: Something in gfly behaves incorrectly.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting! For **security** issues do NOT use this form — see SECURITY.md.
9
+ Please **redact any secrets** (SerpApi keys, cookies) before pasting output.
10
+ - type: input
11
+ id: version
12
+ attributes:
13
+ label: gfly version
14
+ description: Output of `gfly --version`.
15
+ placeholder: "0.1.0"
16
+ validations:
17
+ required: true
18
+ - type: dropdown
19
+ id: backend
20
+ attributes:
21
+ label: Backend
22
+ options: ["google (default)", "serpapi"]
23
+ validations:
24
+ required: true
25
+ - type: input
26
+ id: os
27
+ attributes:
28
+ label: OS / Python
29
+ placeholder: "Ubuntu 24.04 / Python 3.12 (uv)"
30
+ validations:
31
+ required: true
32
+ - type: textarea
33
+ id: command
34
+ attributes:
35
+ label: Command
36
+ description: The exact command you ran (redact secrets).
37
+ render: shell
38
+ validations:
39
+ required: true
40
+ - type: textarea
41
+ id: expected
42
+ attributes:
43
+ label: What you expected vs. what happened
44
+ validations:
45
+ required: true
46
+ - type: textarea
47
+ id: error
48
+ attributes:
49
+ label: Structured error / exit code
50
+ description: Paste the JSON error from stderr and the exit code (`echo $?`).
51
+ render: json
52
+ validations:
53
+ required: false
54
+ - type: textarea
55
+ id: schema
56
+ attributes:
57
+ label: gfly doctor output
58
+ description: Output of `gfly doctor --json` (secrets are redacted automatically).
59
+ render: json
60
+ validations:
61
+ required: false
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: 🔒 Security vulnerability
4
+ url: https://github.com/rnwolfe/gfly/security/advisories/new
5
+ about: Report security issues privately — never via a public issue. See SECURITY.md.
6
+ - name: 💬 Questions & ideas
7
+ url: https://github.com/rnwolfe/gfly/discussions
8
+ about: Ask questions, share recipes, or discuss before filing a feature request.
@@ -0,0 +1,38 @@
1
+ name: Feature request
2
+ description: Suggest a capability or improvement for gfly.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ gfly is **read-only by design** (it searches; it does not book) and **agent-first**
9
+ (stable JSON, structured errors, bounded output). Requests that respect those
10
+ constraints are most likely to land.
11
+ - type: textarea
12
+ id: problem
13
+ attributes:
14
+ label: Problem / use case
15
+ description: What are you trying to do, and what's blocking you today?
16
+ validations:
17
+ required: true
18
+ - type: textarea
19
+ id: proposal
20
+ attributes:
21
+ label: Proposed solution
22
+ description: Command surface, flags, and the output fields you'd expect.
23
+ validations:
24
+ required: true
25
+ - type: dropdown
26
+ id: backend
27
+ attributes:
28
+ label: Which backend(s)?
29
+ multiple: true
30
+ options: ["google", "serpapi", "both / new backend"]
31
+ validations:
32
+ required: false
33
+ - type: textarea
34
+ id: alternatives
35
+ attributes:
36
+ label: Alternatives considered
37
+ validations:
38
+ required: false
@@ -0,0 +1,47 @@
1
+ # Publish checklist (manual operator steps)
2
+
3
+ The repo, README, demo, hygiene, landing page, and release pipeline are ready. These steps require
4
+ your credentials / a browser and can't be automated from here.
5
+
6
+ ## 1. Create the GitHub repo & push
7
+ ```bash
8
+ gh repo create rnwolfe/gfly --public --source=. --remote=origin --push \
9
+ --description "Google Flights for agents — a read-only, JSON-first flight-search CLI an LLM can drive (no API key)."
10
+ ```
11
+
12
+ ## 2. Repo settings (Settings → …)
13
+ - **Topics** (≤20, lowercase-hyphen): `cli` `google-flights` `flight-search` `agent` `llm`
14
+ `agentic` `python` `uv` `travel` `json` `read-only` `serpapi`.
15
+ - **About → website**: the docs/landing URL once hosted.
16
+ - **Social preview**: upload a 1280×640 card (reuse the split-flap board aesthetic from `site/`).
17
+ - **Security → enable Private Vulnerability Reporting** (required for SECURITY.md's flow).
18
+ - **Discussions**: enable (SUPPORT.md and the issue `config.yml` link to it).
19
+ - **Branches → protect `main`**: require PR + status checks (`ci`) + **require review from Code Owners**.
20
+
21
+ ## 3. PyPI Trusted Publishing (no stored secrets)
22
+ - Register the project on PyPI, then add a **Trusted Publisher**: owner `rnwolfe`, repo `gfly`,
23
+ workflow `release.yml`, environment `pypi`. Must match the workflow exactly.
24
+ - Create a repo **Environment** named `pypi`.
25
+
26
+ ## 4. First release (delegate to the `release` skill)
27
+ - Run `/release` to: bump version from Conventional Commits, finalize `CHANGELOG.md`, and create an
28
+ **SSH-signed annotated tag** (→ "Verified" badge). Push the tag → `release.yml` builds, attests
29
+ provenance, publishes to PyPI (OIDC), and creates the GitHub Release with `SHA256SUMS`.
30
+ - Verify: `gh attestation verify <wheel> -R rnwolfe/gfly`.
31
+
32
+ ## 5. Docs site (delegate)
33
+ - `/starlight-docs` — scaffold the Astro Starlight site (Diátaxis IA, `llms.txt`, Pagefind search,
34
+ GitHub Pages deploy + base-path config, AGENTS.md freshness wiring).
35
+ - `/harvest-docs` — fill it from the code (multi-agent: scavenge → IA → per-page writers → reviewers).
36
+ - Auto-generate the Reference quadrant from Click; add the CI freshness gate (regenerate + `git diff --exit-code`).
37
+
38
+ ## 6. Host the landing page
39
+ - `site/index.html` is self-contained (only `gfly.gif` alongside it). Serve via GitHub Pages, or
40
+ `expose` for a quick LAN review. Optionally reserve `gfly.sh`.
41
+
42
+ ## 7. Discoverability (after a tag + >20 stars where required)
43
+ - **awesome-cli-apps** (one app/PR, format `[gfly](url) - Description.`).
44
+ - **awesome-agent-clis** (ComposioHQ) — folder + the bundled `SKILL.md`.
45
+ - **clis.dev** `/submit`; terminaltrove.com.
46
+ - Launch: **Show HN** (`Show HN: gfly – Google Flights for agents (no API key)`, link the repo, be
47
+ present the first 30–60 min); r/commandline; Product Hunt 12:01 PT.
@@ -0,0 +1,15 @@
1
+ <!-- Title must follow Conventional Commits, e.g. `feat: add --max-price filter` -->
2
+
3
+ ## What & why
4
+
5
+ <!-- What does this change and why? Link any issue: Closes #123 -->
6
+
7
+ ## Checklist
8
+
9
+ - [ ] Title uses [Conventional Commits](https://www.conventionalcommits.org/) (`feat:` / `fix:` / `docs:` / …)
10
+ - [ ] `uv run pytest -q` passes locally
11
+ - [ ] Output contract respected: **append-only** fields; stdout=data / stderr=chatter
12
+ - [ ] If the agent-facing schema changed: `SCHEMA_VERSION` bumped + `tests/test_schema_snapshot.py` updated
13
+ - [ ] No secrets via argv; read-only invariant preserved (no new mutations)
14
+ - [ ] `CHANGELOG.md` (Unreleased) and docs / `SKILL.md` updated if behavior changed
15
+ - [ ] Commits signed off (`git commit -s`, DCO)
@@ -0,0 +1,25 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Sync
21
+ run: uv sync --extra dev
22
+ - name: Test (contract gate)
23
+ run: uv run pytest -q
24
+ - name: Schema smoke
25
+ run: uv run gfly schema > /dev/null
@@ -0,0 +1,53 @@
1
+ name: release
2
+
3
+ # Tag-driven release: build → checksums → build-provenance attestation (SLSA L2) →
4
+ # PyPI Trusted Publishing (OIDC) → GitHub Release with artifacts + SHA256SUMS.
5
+ # Prereqs (one-time, manual): configure PyPI Trusted Publishing for owner=rnwolfe,
6
+ # repo=gfly, workflow=release.yml, environment=pypi.
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+
12
+ permissions:
13
+ contents: write # create the GitHub Release
14
+ id-token: write # PyPI OIDC + provenance signing (no stored secrets)
15
+ attestations: write # actions/attest-build-provenance
16
+
17
+ jobs:
18
+ release:
19
+ runs-on: ubuntu-latest
20
+ environment: pypi
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ fetch-depth: 0
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v5
28
+
29
+ - name: Build sdist + wheel
30
+ run: uv build
31
+
32
+ - name: Generate checksums
33
+ run: cd dist && sha256sum * > SHA256SUMS && cat SHA256SUMS
34
+
35
+ # SLSA L2 build provenance. Skipped on private user-owned repos (the action
36
+ # fails there and would block the publish).
37
+ - name: Attest build provenance
38
+ if: ${{ !github.event.repository.private }}
39
+ uses: actions/attest-build-provenance@v1
40
+ with:
41
+ subject-path: "dist/*.whl, dist/*.tar.gz"
42
+
43
+ - name: Publish to PyPI (Trusted Publishing / OIDC)
44
+ run: uv publish
45
+
46
+ - name: Create GitHub Release
47
+ env:
48
+ GH_TOKEN: ${{ github.token }}
49
+ run: |
50
+ gh release create "${GITHUB_REF_NAME}" \
51
+ dist/*.whl dist/*.tar.gz dist/SHA256SUMS \
52
+ --title "${GITHUB_REF_NAME}" \
53
+ --notes "See [CHANGELOG.md](https://github.com/rnwolfe/gfly/blob/main/CHANGELOG.md). Verify provenance: \`gh attestation verify <artifact> -R rnwolfe/gfly\`."
gfly-0.1.0/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ *.egg-info/
7
+ build/
8
+ dist/
9
+
10
+ # Environments
11
+ .venv/
12
+ .env
13
+ .env.*
14
+
15
+ # uv
16
+ # (uv.lock IS committed — do not ignore it)
17
+ .vercel
gfly-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,41 @@
1
+ # AGENTS.md — gfly
2
+
3
+ Conventions for agents editing this repo. (End users driving the CLI want `gfly agent` /
4
+ `src/gfly/SKILL.md` instead.)
5
+
6
+ ## What this is
7
+ `gfly` is an agent-first, **read-only** CLI for searching Google Flights. Scaffolded by
8
+ agent-cli-factory from the Python (Click) template. `spec.md` is the single source of truth.
9
+
10
+ ## Build / test / run
11
+ ```bash
12
+ uv sync --extra dev # install (uv-managed; Python >=3.10)
13
+ uv run pytest -q # contract + behavior tests (must stay green)
14
+ uv run gfly schema # machine-readable command tree
15
+ uv run gfly search JFK LHR --depart 2026-08-01 --json
16
+ python -m gfly --help # module entry also works
17
+ ```
18
+
19
+ ## Layout
20
+ - `src/gfly/cli.py` — Click grammar, `Runtime`, global-flag merge, exit-code mapping. **Contract
21
+ surface; edit deliberately.** Global flags must work in any position (the `_resolve` walk).
22
+ - `src/gfly/output.py` — stdout=data / stderr=chatter, `--format`, `--select`, `--limit`. **Do not edit.**
23
+ - `src/gfly/errors.py` — exit-code table + structured `AppError` (incl. `BLOCKED` 20, `SCHEMA_DRIFT` 21).
24
+ - `src/gfly/throttle.py` — persistent cross-process politeness/circuit-breaker (real infra).
25
+ - `src/gfly/backend.py` — **PLACEHOLDER** stub data. `cli-implement` replaces with fast-flights
26
+ (google) + SerpApi (serpapi), behind the same normalized shapes.
27
+ - `src/gfly/auth.py` — **PLACEHOLDER** credential resolution (env only today; keyring later).
28
+ - `src/gfly/SKILL.md` — embedded; printed by `gfly agent`. Regenerate when the surface changes.
29
+
30
+ ## Rules
31
+ - **Read-only tool.** No command mutates. The `--allow-mutations` gate + `Runtime.guard` are kept
32
+ for contract uniformity but unused; don't add mutations without revisiting `spec.md`.
33
+ - **Output contract is append-only** (contract §10). New fields OK; never rename/remove. Bump
34
+ `SCHEMA_VERSION` (in `backend.py`) on any breaking shape change and update the snapshot test.
35
+ - **Secrets via stdin/env, never argv** (contract §7).
36
+ - **Heavy imports stay lazy** — inside backend functions, never at module top (keeps `--help`/`schema` fast).
37
+ - Keep `uv run pytest -q` green before every commit; the `schema` test is the contract gate.
38
+
39
+ ## Handoff state
40
+ This is a **scaffold**: it compiles, runs on stub data, and passes contract tests. Next stage is
41
+ `cli-implement` — wire the real engines + auth, replacing `backend.py` / `auth.py`.
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-23
11
+
12
+ First public release.
13
+
14
+ ### Added
15
+ - `search` (one-way / round-trip), `dates` (price calendar), `multi` (multi-city), and
16
+ `airports search` (offline IATA resolution) commands.
17
+ - Swappable backends: `google` (reverse-engineered, zero-auth, default) and `serpapi` (live JSON, keyed).
18
+ - Agent-CLI contract: stable versioned JSON envelope (`schemaVersion`), `schema` self-description,
19
+ embedded `agent` SKILL.md, `--select`/`--limit`/`--offset` token bounding, `--format json|plain|tsv`.
20
+ - Semantic exit codes incl. `BLOCKED` (20) and `SCHEMA_DRIFT` (21); structured errors with
21
+ `retryAfterSeconds` on throttle/block.
22
+ - Persistent, cross-process politeness throttle (min-interval + circuit breaker) with fail-fast
23
+ default and `--wait` opt-in.
24
+ - `auth login|status|logout` (OS keyring + `0600` file fallback, stdin-only secrets); real `doctor`.
25
+ - Prompt-injection hardening: third-party text sanitized and fenced as untrusted by default.
26
+
27
+ [Unreleased]: https://github.com/rnwolfe/gfly/compare/v0.1.0...HEAD
28
+ [0.1.0]: https://github.com/rnwolfe/gfly/releases/tag/v0.1.0
@@ -0,0 +1,22 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use gfly, please cite it using these metadata."
3
+ title: "gfly: an agent-first, read-only Google Flights CLI"
4
+ abstract: >-
5
+ gfly is a read-only, JSON-first command-line tool for searching Google Flights,
6
+ engineered to be driven by LLM agents: stable versioned output schema, machine-readable
7
+ self-description, semantic exit codes, token-bounded output, a persistent politeness
8
+ throttle, and a swappable backend (reverse-engineered Google Flights or SerpApi).
9
+ type: software
10
+ authors:
11
+ - family-names: Wolfe
12
+ given-names: Ryan
13
+ license: "MIT OR Apache-2.0"
14
+ repository-code: "https://github.com/rnwolfe/gfly"
15
+ url: "https://github.com/rnwolfe/gfly"
16
+ keywords:
17
+ - google-flights
18
+ - cli
19
+ - agent
20
+ - llm
21
+ - flight-search
22
+ - travel
@@ -0,0 +1,99 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a
6
+ harassment-free experience for everyone, regardless of age, body size, visible or invisible
7
+ disability, ethnicity, sex characteristics, gender identity and expression, level of experience,
8
+ education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or
9
+ sexual identity and orientation.
10
+
11
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
12
+ healthy community.
13
+
14
+ ## Our Standards
15
+
16
+ Examples of behavior that contributes to a positive environment for our community include:
17
+
18
+ * Demonstrating empathy and kindness toward other people
19
+ * Being respectful of differing opinions, viewpoints, and experiences
20
+ * Giving and gracefully accepting constructive feedback
21
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the
22
+ experience
23
+ * Focusing on what is best not just for us as individuals, but for the overall community
24
+
25
+ Examples of unacceptable behavior include:
26
+
27
+ * The use of sexualized language or imagery, and sexual attention or advances of any kind
28
+ * Trolling, insulting or derogatory comments, and personal or political attacks
29
+ * Public or private harassment
30
+ * Publishing others' private information, such as a physical or email address, without their explicit
31
+ permission
32
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
33
+
34
+ ## Enforcement Responsibilities
35
+
36
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and
37
+ will take appropriate and fair corrective action in response to any behavior that they deem
38
+ inappropriate, threatening, offensive, or harmful.
39
+
40
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code,
41
+ wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will
42
+ communicate reasons for moderation decisions when appropriate.
43
+
44
+ ## Scope
45
+
46
+ This Code of Conduct applies within all community spaces, and also applies when an individual is
47
+ officially representing the community in public spaces.
48
+
49
+ ## Enforcement
50
+
51
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community
52
+ leaders responsible for enforcement at **rn.wolfe@gmail.com**. All complaints will be reviewed and
53
+ investigated promptly and fairly.
54
+
55
+ All community leaders are obligated to respect the privacy and security of the reporter of any
56
+ incident.
57
+
58
+ ## Enforcement Guidelines
59
+
60
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any
61
+ action they deem in violation of this Code of Conduct:
62
+
63
+ ### 1. Correction
64
+
65
+ **Community Impact:** Use of inappropriate language or other behavior deemed unprofessional or unwelcome.
66
+
67
+ **Consequence:** A private, written warning from community leaders, providing clarity around the nature
68
+ of the violation and an explanation of why the behavior was inappropriate. A public apology may be
69
+ requested.
70
+
71
+ ### 2. Warning
72
+
73
+ **Community Impact:** A violation through a single incident or series of actions.
74
+
75
+ **Consequence:** A warning with consequences for continued behavior. No interaction with the people
76
+ involved for a specified period of time. Violating these terms may lead to a temporary or permanent ban.
77
+
78
+ ### 3. Temporary Ban
79
+
80
+ **Community Impact:** A serious violation of community standards, including sustained inappropriate
81
+ behavior.
82
+
83
+ **Consequence:** A temporary ban from any sort of interaction or public communication with the
84
+ community for a specified period of time. Violating these terms may lead to a permanent ban.
85
+
86
+ ### 4. Permanent Ban
87
+
88
+ **Community Impact:** Demonstrating a pattern of violation of community standards, harassment of an
89
+ individual, or aggression toward or disparagement of classes of individuals.
90
+
91
+ **Consequence:** A permanent ban from any sort of public interaction within the community.
92
+
93
+ ## Attribution
94
+
95
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 3.0, available at
96
+ <https://www.contributor-covenant.org/version/3/0/code_of_conduct/>. Contributor Covenant is stewarded
97
+ by the Organization for Ethical Source and licensed under CC BY-SA 4.0.
98
+
99
+ [homepage]: https://www.contributor-covenant.org
@@ -0,0 +1,47 @@
1
+ # Contributing to gfly
2
+
3
+ Thanks for helping! gfly is an agent-first, read-only Google Flights CLI. `spec.md` is the source
4
+ of truth for what it is; the **agent-CLI contract** (read-only, stable JSON, structured errors,
5
+ bounded output) is non-negotiable — keep it intact.
6
+
7
+ ## Setup
8
+
9
+ ```bash
10
+ uv sync --extra dev # Python >= 3.10, uv-managed
11
+ uv run pytest -q # tests must stay green
12
+ uv run gfly schema # machine-readable command tree
13
+ ```
14
+
15
+ ## Develop
16
+
17
+ - **Layout:** `src/gfly/cli.py` (Click grammar + runtime), `backend.py` (google/serpapi engines),
18
+ `throttle.py` (persistent politeness), `auth.py` (keyring), `output.py` (output discipline —
19
+ don't break stdout=data/stderr=chatter), `errors.py` (exit-code table). See `AGENTS.md`.
20
+ - **Heavy imports stay lazy** (inside the functions that use them) so `--help`/`schema` stay fast.
21
+ - **Read-only:** no command may mutate remote state. Don't add mutations without revisiting `spec.md`.
22
+ - **Output contract is append-only:** add fields freely; never rename/remove. A breaking shape change
23
+ means bumping `SCHEMA_VERSION` and updating `tests/test_schema_snapshot.py` in the same PR.
24
+ - **Secrets:** stdin/env only, never argv (contract §7).
25
+
26
+ ## Tests
27
+
28
+ ```bash
29
+ uv run pytest -q
30
+ ```
31
+
32
+ Network is mocked in tests (`tests/conftest.py` monkeypatches the two backend entry points). The
33
+ **schema-snapshot test is the CI gate** — if it fails, you changed the agent-facing contract; make
34
+ that a deliberate, reviewed diff.
35
+
36
+ ## Pull requests
37
+
38
+ - Use **[Conventional Commits](https://www.conventionalcommits.org/)** (`feat:`, `fix:`, `docs:`,
39
+ `refactor:`, `test:`, `chore:`) — they drive the changelog and semver bump.
40
+ - Sign off your commits (**DCO**): `git commit -s`. No CLA.
41
+ - Keep PRs focused; update `CHANGELOG.md` (Unreleased) and docs/`SKILL.md` when behavior changes.
42
+ - Green CI required: tests + `gfly schema` smoke.
43
+
44
+ ## Reporting bugs / security
45
+
46
+ - Bugs: open an issue (the form asks for `gfly --version`, OS, backend, repro, and the JSON error).
47
+ - Security: **do not** open a public issue — see [SECURITY.md](SECURITY.md).