crewforge 0.2.0__tar.gz → 0.2.2__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 (79) hide show
  1. {crewforge-0.2.0/src/crewforge.egg-info → crewforge-0.2.2}/PKG-INFO +101 -57
  2. {crewforge-0.2.0 → crewforge-0.2.2}/README.md +86 -53
  3. {crewforge-0.2.0 → crewforge-0.2.2}/pyproject.toml +46 -4
  4. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/__init__.py +1 -1
  5. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/citations.py +3 -2
  6. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/cli.py +15 -6
  7. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/language.py +3 -1
  8. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/agents.py +3 -1
  9. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/agents.py +6 -4
  10. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/ingest.py +2 -2
  11. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/pipeline.py +13 -7
  12. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/agents.py +2 -2
  13. {crewforge-0.2.0 → crewforge-0.2.2/src/crewforge.egg-info}/PKG-INFO +101 -57
  14. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge.egg-info/requires.txt +2 -0
  15. {crewforge-0.2.0 → crewforge-0.2.2}/LICENSE +0 -0
  16. {crewforge-0.2.0 → crewforge-0.2.2}/setup.cfg +0 -0
  17. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/__main__.py +0 -0
  18. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/cli.py +0 -0
  19. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/cli_support.py +0 -0
  20. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/__init__.py +0 -0
  21. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/agent.py +0 -0
  22. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/client.py +0 -0
  23. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/config.py +0 -0
  24. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/crew.py +0 -0
  25. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/errors.py +0 -0
  26. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/parsing.py +0 -0
  27. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/providers.py +0 -0
  28. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/core/usage.py +0 -0
  29. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/py.typed +0 -0
  30. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/__init__.py +0 -0
  31. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/__init__.py +0 -0
  32. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/agents.py +0 -0
  33. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/disclosure.py +0 -0
  34. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/locales/en.yaml +0 -0
  35. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/locales/ms.yaml +0 -0
  36. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/pipeline.py +0 -0
  37. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/report.py +0 -0
  38. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/academic/settings.py +0 -0
  39. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/__init__.py +0 -0
  40. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/analytics.py +0 -0
  41. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/appcheck.py +0 -0
  42. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/cli.py +0 -0
  43. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/formatters.py +0 -0
  44. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/generator.py +0 -0
  45. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/pipeline.py +0 -0
  46. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/scaffold.py +0 -0
  47. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/docker-compose.j2 +0 -0
  48. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/dockerfile.j2 +0 -0
  49. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/env.example.j2 +0 -0
  50. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/fastapi_main.j2 +0 -0
  51. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/model.j2 +0 -0
  52. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/requirements.j2 +0 -0
  53. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/route.j2 +0 -0
  54. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/templates/test.j2 +0 -0
  55. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/api/utils.py +0 -0
  56. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/__init__.py +0 -0
  57. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/agents.py +0 -0
  58. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/cli.py +0 -0
  59. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/export.py +0 -0
  60. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/pipeline.py +0 -0
  61. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/settings.py +0 -0
  62. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/content/text.py +0 -0
  63. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/__init__.py +0 -0
  64. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/cli.py +0 -0
  65. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/export.py +0 -0
  66. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/index.py +0 -0
  67. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/retrieve.py +0 -0
  68. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/docs/settings.py +0 -0
  69. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/__init__.py +0 -0
  70. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/cli.py +0 -0
  71. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/pipeline.py +0 -0
  72. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/report.py +0 -0
  73. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge/recipes/review/sources.py +0 -0
  74. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge.egg-info/SOURCES.txt +0 -0
  75. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge.egg-info/dependency_links.txt +0 -0
  76. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge.egg-info/entry_points.txt +0 -0
  77. {crewforge-0.2.0 → crewforge-0.2.2}/src/crewforge.egg-info/top_level.txt +0 -0
  78. {crewforge-0.2.0 → crewforge-0.2.2}/tests/test_honest_failure_e2e.py +0 -0
  79. {crewforge-0.2.0 → crewforge-0.2.2}/tests/test_readme.py +0 -0
@@ -1,17 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crewforge
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: One crew of LLM agents, five recipes: code review, document Q&A, API scaffolding, content and honest academic-writing help — on any OpenAI-compatible provider.
5
5
  Author: Muhammad Aiman
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/aimanmalib/crewforge
8
- Project-URL: Repository, https://github.com/aimanmalib/crewforge
9
- Project-URL: Issues, https://github.com/aimanmalib/crewforge/issues
8
+ Project-URL: Documentation, https://github.com/aimanmalib/crewforge#readme
10
9
  Project-URL: Changelog, https://github.com/aimanmalib/crewforge/blob/main/CHANGELOG.md
11
- Keywords: llm,agents,multi-agent,openai,code-review,rag,fastapi,content-generation,academic-writing,cli
10
+ Project-URL: Issues, https://github.com/aimanmalib/crewforge/issues
11
+ Project-URL: Source, https://github.com/aimanmalib/crewforge
12
+ Project-URL: Security, https://github.com/aimanmalib/crewforge/security/policy
13
+ Keywords: llm,agents,multi-agent,ai-agents,openai,openai-compatible,code-review,rag,document-qa,fastapi,api-scaffolding,content-generation,academic-writing,cli
12
14
  Classifier: Development Status :: 3 - Alpha
13
15
  Classifier: Environment :: Console
14
16
  Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Education
18
+ Classifier: Natural Language :: English
19
+ Classifier: Natural Language :: Malay
15
20
  Classifier: Operating System :: OS Independent
16
21
  Classifier: Programming Language :: Python :: 3
17
22
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -19,8 +24,12 @@ Classifier: Programming Language :: Python :: 3.10
19
24
  Classifier: Programming Language :: Python :: 3.11
20
25
  Classifier: Programming Language :: Python :: 3.12
21
26
  Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: 3.14
28
+ Classifier: Topic :: Education
22
29
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
30
+ Classifier: Topic :: Software Development :: Code Generators
23
31
  Classifier: Topic :: Software Development :: Quality Assurance
32
+ Classifier: Topic :: Text Processing :: General
24
33
  Classifier: Typing :: Typed
25
34
  Requires-Python: >=3.10
26
35
  Description-Content-Type: text/markdown
@@ -41,18 +50,27 @@ Requires-Dist: pytest>=8.0; extra == "dev"
41
50
  Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
42
51
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
43
52
  Requires-Dist: ruff>=0.6; extra == "dev"
53
+ Requires-Dist: mypy>=1.10; extra == "dev"
54
+ Requires-Dist: types-PyYAML>=6.0; extra == "dev"
44
55
  Requires-Dist: fastapi>=0.110; extra == "dev"
45
56
  Requires-Dist: httpx>=0.27; extra == "dev"
46
57
  Dynamic: license-file
47
58
 
48
- # crewforge
59
+ <p align="center"><img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/brand/logo-512.png" width="112" height="112" alt="crewforge"></p>
60
+
61
+ <h1 align="center">crewforge</h1>
62
+
63
+ <p align="center"><a href="https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml"><img src="https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://pypi.org/project/crewforge/"><img src="https://img.shields.io/pypi/v/crewforge" alt="PyPI"></a> <a href="https://scorecard.dev/viewer/?uri=github.com/aimanmalib/crewforge"><img src="https://api.scorecard.dev/projects/github.com/aimanmalib/crewforge/badge" alt="OpenSSF Scorecard"></a> <img src="https://img.shields.io/badge/python-3.10%E2%80%933.14-blue" alt="Python 3.10–3.14"> <a href="https://github.com/aimanmalib/crewforge/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License: MIT"></a></p>
49
64
 
50
- **One crew of LLM agents, five recipes — code review, document Q&A, API scaffolding, content and honest academic-writing help — on any OpenAI-compatible provider, and it fails loudly instead of pretending.**
65
+ <p align="center"><b>One crew of LLM agents, five recipes, any OpenAI-compatible provider.</b> · <i>Satu kru ejen LLM, lima resipi, mana-mana penyedia serasi OpenAI.</i><br>
66
+ <a href="https://pypi.org/project/crewforge/">PyPI</a> · <a href="https://github.com/aimanmalib/crewforge/blob/main/CHANGELOG.md">changelog</a> · <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md">porting notes</a> · Built in Malaysia by <a href="https://aimanmalib.com">Aiman Malib</a></p>
51
67
 
52
- [![CI](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml/badge.svg)](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml)
53
- [![PyPI](https://img.shields.io/pypi/v/crewforge)](https://pypi.org/project/crewforge/)
54
- ![Python 3.10–3.13](https://img.shields.io/badge/python-3.10%E2%80%933.13-blue)
55
- ![License: MIT](https://img.shields.io/badge/license-MIT-green)
68
+ crewforge is a command-line tool and Python library that runs a crew of LLM agents through five recipes: code
69
+ review, document Q&A, API scaffolding, content writing and honest academic-writing help. It works with any
70
+ OpenAI-compatible provider, reads API keys only from the environment, and fails loudly instead of pretending: a run
71
+ in which an agent produced nothing exits with code 1 and names that agent.
72
+
73
+ [Bahasa Melayu ↓](#bahasa-melayu)
56
74
 
57
75
  ```bash
58
76
  crewforge review src/ # six reviewers + a summary
@@ -69,7 +87,7 @@ crewforge academic feedback draft.md --brief brief.md --language ms
69
87
 
70
88
  ## Why one crew, many recipes
71
89
 
72
- crewforge started as four separate command-line tools —
90
+ crewforge started as four separate command-line tools:
73
91
  [codeguard](https://github.com/aimanmalib/codeguard),
74
92
  [documind](https://github.com/aimanmalib/documind),
75
93
  [apiforge](https://github.com/aimanmalib/apiforge) and
@@ -83,7 +101,7 @@ showed the copies had drifted and that each hid its failures differently:
83
101
  - apiforge scored a spec with **no endpoints** 100/100;
84
102
  - contentforge wrote the real API key into its YAML file, and sent `${OPENAI_API_KEY}` literally as the key.
85
103
 
86
- crewforge keeps one core — so a fix lands once — and turns each tool into a
104
+ crewforge keeps one core, so a fix lands once, and turns each tool into a
87
105
  *recipe*: a set of agents plus the steps that connect them. The same core rules
88
106
  apply to every recipe: non-streaming requests, bounded retries, usage numbers
89
107
  taken only from the provider, keys only from the environment, and no run that
@@ -161,6 +179,11 @@ total 7 calls 22,366 tokens (provider-reported)
161
179
 
162
180
  ### `ask` and `summarize`
163
181
 
182
+ <p align="center">
183
+ <img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/demo/ask.gif" alt="Terminal demo: crewforge ask --verify answers two questions about a short project brief with [Source N] citations; the second answer says the document does not give the cost, and both answers are fact-checked claim by claim" width="900">
184
+ </p>
185
+ <p align="center"><sub>A real run of <code>crewforge ask --verify</code>: the answerer cites the passages it used as <code>[Source N]</code>, a second agent checks every claim against those passages, and the question the document does not answer is answered as such. Provider-reported tokens. Recorded with <a href="https://github.com/charmbracelet/vhs">VHS</a> from <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/demo/ask.tape"><code>docs/demo/ask.tape</code></a> (the document is invented) using Gemini 3.8 Flash through an OpenAI-compatible gateway (the <code>custom</code> provider). The wait for the model is shown in full.</sub></p>
186
+
164
187
  ```text
165
188
  $ crewforge ask kestrel.md -q "How many sensors does Project Kestrel use, and who leads it?" --verify --export session.md
166
189
  ✓ ingested kestrel.md (1 chunks)
@@ -232,6 +255,11 @@ Indonesian forms such as *karena* or *kualitas*). Every step is recorded
232
255
  (file names and hashes, never content or keys) so `disclose` can write the
233
256
  AI-use statement many universities now ask for.
234
257
 
258
+ <p align="center">
259
+ <img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/demo/academic.gif" alt="Terminal demo: crewforge academic feedback --language ms comments in Malay on a student's essay draft, quoting the sentences it refers to, then a no-AI citation check lists (Lim, 2024) as cited in the text but missing from the reference list" width="900">
260
+ </p>
261
+ <p align="center"><sub>A real run of <code>crewforge academic feedback --language ms</code> on a short essay draft in Malay. Each comment quotes the student's own sentence and suggests a fix; nothing is rewritten. The citation check after it uses no AI: it finds <em>(Lim, 2024)</em> cited in the text but missing from the reference list. Only the <code>argument</code> reviewer is run so the result fits on one screen. Recorded with <a href="https://github.com/charmbracelet/vhs">VHS</a> from <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/demo/academic.tape"><code>docs/demo/academic.tape</code></a> (draft, brief and reference are invented) using Gemini 3.8 Flash through an OpenAI-compatible gateway (the <code>custom</code> provider). The wait for the model is shown in full.</sub></p>
262
+
235
263
  ```text
236
264
  $ crewforge academic plan brief.md -f markdown # a 1,200-word TSL3013 essay brief
237
265
  # Assignment plan: Regulated Integration over Prohibition: ...
@@ -268,13 +296,13 @@ Saya menggunakan crewforge academic (versi 0.1.0), sebuah alat berbantukan AI, d
268
296
  - 22 Oktober 2026: Perancangan: alat ini menukar taklimat tugasan (brief-ms.md) kepada 3 soalan kajian, rangka 5 bahagian dan peruntukan perkataan. ...
269
297
  ```
270
298
 
271
- How sources are labelled — there is no "verified" status, because the tool
299
+ How sources are labelled: there is no "verified" status, because the tool
272
300
  cannot check that a source exists:
273
301
 
274
302
  | Source | Label |
275
303
  |---|---|
276
304
  | You supplied it | *supplied by you (not checked online)* |
277
- | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED** — check it |
305
+ | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED**: check it |
278
306
  | The model proposed it with none of those | **LIKELY FABRICATED** |
279
307
  | The model added a DOI/URL/ISBN to a reference you supplied | **UNVERIFIED**, naming the added identifier |
280
308
 
@@ -309,47 +337,20 @@ An unknown provider name is a configuration error, not a silent fallback.
309
337
 
310
338
  ## Architecture
311
339
 
312
- ```mermaid
313
- flowchart LR
314
- CLI["crewforge CLI<br/>review · ask · summarize · ingest<br/>scaffold · validate · write<br/>academic plan · feedback · cite · disclose"] --> R
315
-
316
- subgraph R["Recipes (src/crewforge/recipes)"]
317
- RV[review]
318
- DC[docs]
319
- AP[api]
320
- CT[content]
321
- AC[academic]
322
- end
323
-
324
- R --> CREW
325
-
326
- subgraph CORE["Core (src/crewforge/core)"]
327
- CREW["Crew<br/>step() · parallel()<br/>names failed agents"]
328
- AG["Agent<br/>ask() · ask_json()<br/>rejects empty output"]
329
- PA["parsing<br/>tolerant JSON + schema"]
330
- CL["LLMClient<br/>stream=false · retries 429/5xx only"]
331
- US["UsageTracker<br/>provider-reported tokens"]
332
- CF["LLMConfig + provider presets<br/>keys from env only"]
333
- CREW --> AG
334
- AG --> PA
335
- AG --> CL
336
- AG --> US
337
- CL --> CF
338
- end
339
-
340
- CL -->|"/chat/completions"| P[("Any OpenAI-compatible provider")]
341
- ```
340
+ <p align="center"><img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/architecture.png" alt="Architecture: the CLI dispatches to five recipes; each recipe builds a Crew of Agents on the shared core (parsing, LLMClient, LLMConfig, UsageTracker), which talks to any OpenAI-compatible provider over /chat/completions" width="100%"></p>
341
+
342
+ <sub>Source: [docs/architecture.mmd](https://github.com/aimanmalib/crewforge/blob/main/docs/architecture.mmd) (re-render with `scripts/render_architecture.py`).</sub>
342
343
 
343
- - **`core/client.py`** — one async client. It always sends `"stream": false`,
344
+ - **`core/client.py`**: one async client. It always sends `"stream": false`,
344
345
  retries only HTTP 429, 5xx and network/timeout errors (with capped exponential
345
346
  backoff that honours `Retry-After`), and fails on the first 401/403/4xx with a
346
347
  message naming the variable to fix. The key is only ever a request header.
347
- - **`core/agent.py`** — an agent is a role, a system prompt and per-agent
348
+ - **`core/agent.py`**: an agent is a role, a system prompt and per-agent
348
349
  sampling defaults. `ask()` records usage and raises if the reply is blank;
349
350
  `ask_json()` additionally requires parseable JSON (optionally a pydantic schema).
350
- - **`core/crew.py`** — runs agents as sequential steps or parallel stages and
351
+ - **`core/crew.py`**: runs agents as sequential steps or parallel stages and
351
352
  turns any failure into a `CrewFailedError` that lists every failed agent.
352
- - **`core/usage.py`** — per-agent calls, tokens, retries and latency, taken from
353
+ - **`core/usage.py`**: per-agent calls, tokens, retries and latency, taken from
353
354
  the provider's `usage` object. Calls without usage are counted as "not reported",
354
355
  never estimated.
355
356
 
@@ -414,22 +415,23 @@ watching them fail; see [docs/porting.md](https://github.com/aimanmalib/crewforg
414
415
 
415
416
  crewforge was built with AI coding assistance (Claude Code) under the
416
417
  direction of its author, Muhammad Aiman. It consolidates four earlier tools by
417
- the same author — [codeguard](https://github.com/aimanmalib/codeguard),
418
+ the same author ([codeguard](https://github.com/aimanmalib/codeguard),
418
419
  [documind](https://github.com/aimanmalib/documind),
419
420
  [apiforge](https://github.com/aimanmalib/apiforge) and
420
- [contentforge](https://github.com/aimanmalib/contentforge) — whose test suites
421
+ [contentforge](https://github.com/aimanmalib/contentforge)), whose test suites
421
422
  were carried over into this repository (see [docs/porting.md](https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md)
422
423
  for what was ported, re-targeted at the shared core, or dropped and why).
423
424
 
424
425
  The evidence that it works is meant to be checkable: the test suite (run
425
- `pytest`; it needs no key and no network), CI on Python 3.10–3.13, and the
426
+ `pytest`; it needs no key and no network), CI on Python 3.10–3.14, and the
426
427
  real-provider runs shown above, two of which found bugs that were fixed with
427
428
  tests before the output was recorded.
428
429
 
429
430
  ## Contributing
430
431
 
431
- See [CONTRIBUTING.md](https://github.com/aimanmalib/crewforge/blob/main/CONTRIBUTING.md). In short: `pip install -e ".[dev,docs]"`,
432
- `pytest`, `ruff check . && ruff format --check .`, and every new failure path
432
+ See [CONTRIBUTING.md](https://github.com/aimanmalib/crewforge/blob/main/CONTRIBUTING.md). In short: `uv sync --extra dev --extra docs`
433
+ (versions from the committed `uv.lock`, as in CI), `uv run pytest`,
434
+ `uv run ruff check . && uv run ruff format --check .`, and every new failure path
433
435
  needs a test that fails if the guard is removed.
434
436
 
435
437
  ## Security
@@ -439,10 +441,52 @@ Please report vulnerabilities privately through
439
441
  See [SECURITY.md](https://github.com/aimanmalib/crewforge/blob/main/SECURITY.md) for scope notes (what is sent to your provider,
440
442
  and what `validate` executes).
441
443
 
442
- ## License
444
+ ## Bahasa Melayu
445
+
446
+ crewforge ialah alat baris arahan dan pustaka Python yang menjalankan satu kru ejen LLM melalui lima resipi:
447
+ semakan kod (`review`), soal jawab dokumen (`ask`, `summarize`), penjanaan projek API FastAPI (`scaffold`,
448
+ `validate`), penulisan kandungan (`write`) dan bantuan penulisan akademik yang jujur (`academic`). Ia berfungsi
449
+ dengan mana-mana penyedia serasi OpenAI, termasuk model tempatan melalui Ollama atau llama.cpp. Kunci API hanya
450
+ dibaca dari pemboleh ubah persekitaran, tidak pernah ditulis ke fail.
451
+
452
+ **Mengapa satu kru.** crewforge menggabungkan empat alat lama (codeguard, documind, apiforge, contentforge) yang
453
+ masing-masing menyimpan salinan sendiri bagi rangka yang sama. Salinan itu sudah berbeza dan setiap satu
454
+ menyembunyikan kegagalan dengan cara sendiri. Kini teras dikongsi: pembaikan dibuat sekali, dan setiap alat menjadi
455
+ resipi di atas teras itu.
456
+
457
+ **Gagal dengan jelas.** Larian yang ejennya memulangkan jawapan kosong atau tidak boleh dihurai, atau pengesah
458
+ yang tidak menyemak apa-apa, keluar dengan kod 1 dan menamakan ejen itu. Kod keluar: `0` berjaya, `1` larian
459
+ gagal, `2` ralat penggunaan atau konfigurasi. Bilangan token diambil daripada laporan penyedia sahaja, tidak
460
+ dianggar.
461
+
462
+ **Resipi `academic`.** Membantu pelajar dan penyelidik dengan kerja mereka **sendiri**: merancang dari taklimat
463
+ tugasan, memberi ulasan atas draf tanpa menulis semula, memformat rujukan yang diberi pengguna (APA 7, IEEE,
464
+ Harvard) tanpa menambah rujukan baharu, dan menulis pernyataan penggunaan AI daripada rekod langkah yang
465
+ dijalankan. Output dalam bahasa Inggeris atau Bahasa Melayu (`--language ms`); output BM disemak untuk bentuk
466
+ Indonesia seperti *karena* atau *kualitas*. Sumber yang dicadangkan model dilabel UNVERIFIED, atau LIKELY
467
+ FABRICATED jika tiada DOI, URL atau ISBN. Tiada ciri "humanise" atau mengelak pengesan AI.
468
+
469
+ **Pasang dan guna.**
470
+
471
+ ```bash
472
+ pip install crewforge # Python 3.10 hingga 3.14
473
+ pip install "crewforge[docs,api]" # pilihan: PDF/DOCX dan semakan aplikasi FastAPI
474
+ export OPENAI_API_KEY=sk-... # atau penyedia lain, lihat `crewforge providers`
475
+ crewforge review src/
476
+ crewforge academic feedback draf.md --brief taklimat.md --language ms
477
+ ```
478
+
479
+ Senarai penyedia, contoh setiap resipi dan seni bina ada dalam bahagian bahasa Inggeris di atas
480
+ ([Providers](https://github.com/aimanmalib/crewforge#providers), [Recipes](https://github.com/aimanmalib/crewforge#recipes)).
481
+
482
+ ## Licence
483
+
484
+ [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman. Made by [Aiman Malib](https://aimanmalib.com), Malaysia.
485
+
486
+ ## Work with Sasa Teguh
443
487
 
444
- [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman
488
+ [![Work with Sasa Teguh: book a call](https://raw.githubusercontent.com/sasateguh/.github/main/assets/work-with-sasa-teguh.png)](https://sasateguh.com/book)
445
489
 
446
- ---
490
+ I built this project. I run [Sasa Teguh](https://sasateguh.com), a studio in Malaysia that builds websites found on Google and in AI answers, and sets up systems like this one for businesses.
447
491
 
448
- Built by [Sasa Teguh](https://sasateguh.com) · Malaysia
492
+ [Book a call](https://sasateguh.com/book) · hello@sasateguh.com · [More from Sasa Teguh](https://github.com/sasateguh)
@@ -1,11 +1,18 @@
1
- # crewforge
1
+ <p align="center"><img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/brand/logo-512.png" width="112" height="112" alt="crewforge"></p>
2
2
 
3
- **One crew of LLM agents, five recipes — code review, document Q&A, API scaffolding, content and honest academic-writing help — on any OpenAI-compatible provider, and it fails loudly instead of pretending.**
3
+ <h1 align="center">crewforge</h1>
4
4
 
5
- [![CI](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml/badge.svg)](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml)
6
- [![PyPI](https://img.shields.io/pypi/v/crewforge)](https://pypi.org/project/crewforge/)
7
- ![Python 3.10–3.13](https://img.shields.io/badge/python-3.10%E2%80%933.13-blue)
8
- ![License: MIT](https://img.shields.io/badge/license-MIT-green)
5
+ <p align="center"><a href="https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml"><img src="https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://pypi.org/project/crewforge/"><img src="https://img.shields.io/pypi/v/crewforge" alt="PyPI"></a> <a href="https://scorecard.dev/viewer/?uri=github.com/aimanmalib/crewforge"><img src="https://api.scorecard.dev/projects/github.com/aimanmalib/crewforge/badge" alt="OpenSSF Scorecard"></a> <img src="https://img.shields.io/badge/python-3.10%E2%80%933.14-blue" alt="Python 3.10–3.14"> <a href="https://github.com/aimanmalib/crewforge/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License: MIT"></a></p>
6
+
7
+ <p align="center"><b>One crew of LLM agents, five recipes, any OpenAI-compatible provider.</b> · <i>Satu kru ejen LLM, lima resipi, mana-mana penyedia serasi OpenAI.</i><br>
8
+ <a href="https://pypi.org/project/crewforge/">PyPI</a> · <a href="https://github.com/aimanmalib/crewforge/blob/main/CHANGELOG.md">changelog</a> · <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md">porting notes</a> · Built in Malaysia by <a href="https://aimanmalib.com">Aiman Malib</a></p>
9
+
10
+ crewforge is a command-line tool and Python library that runs a crew of LLM agents through five recipes: code
11
+ review, document Q&A, API scaffolding, content writing and honest academic-writing help. It works with any
12
+ OpenAI-compatible provider, reads API keys only from the environment, and fails loudly instead of pretending: a run
13
+ in which an agent produced nothing exits with code 1 and names that agent.
14
+
15
+ [Bahasa Melayu ↓](#bahasa-melayu)
9
16
 
10
17
  ```bash
11
18
  crewforge review src/ # six reviewers + a summary
@@ -22,7 +29,7 @@ crewforge academic feedback draft.md --brief brief.md --language ms
22
29
 
23
30
  ## Why one crew, many recipes
24
31
 
25
- crewforge started as four separate command-line tools —
32
+ crewforge started as four separate command-line tools:
26
33
  [codeguard](https://github.com/aimanmalib/codeguard),
27
34
  [documind](https://github.com/aimanmalib/documind),
28
35
  [apiforge](https://github.com/aimanmalib/apiforge) and
@@ -36,7 +43,7 @@ showed the copies had drifted and that each hid its failures differently:
36
43
  - apiforge scored a spec with **no endpoints** 100/100;
37
44
  - contentforge wrote the real API key into its YAML file, and sent `${OPENAI_API_KEY}` literally as the key.
38
45
 
39
- crewforge keeps one core — so a fix lands once — and turns each tool into a
46
+ crewforge keeps one core, so a fix lands once, and turns each tool into a
40
47
  *recipe*: a set of agents plus the steps that connect them. The same core rules
41
48
  apply to every recipe: non-streaming requests, bounded retries, usage numbers
42
49
  taken only from the provider, keys only from the environment, and no run that
@@ -114,6 +121,11 @@ total 7 calls 22,366 tokens (provider-reported)
114
121
 
115
122
  ### `ask` and `summarize`
116
123
 
124
+ <p align="center">
125
+ <img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/demo/ask.gif" alt="Terminal demo: crewforge ask --verify answers two questions about a short project brief with [Source N] citations; the second answer says the document does not give the cost, and both answers are fact-checked claim by claim" width="900">
126
+ </p>
127
+ <p align="center"><sub>A real run of <code>crewforge ask --verify</code>: the answerer cites the passages it used as <code>[Source N]</code>, a second agent checks every claim against those passages, and the question the document does not answer is answered as such. Provider-reported tokens. Recorded with <a href="https://github.com/charmbracelet/vhs">VHS</a> from <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/demo/ask.tape"><code>docs/demo/ask.tape</code></a> (the document is invented) using Gemini 3.8 Flash through an OpenAI-compatible gateway (the <code>custom</code> provider). The wait for the model is shown in full.</sub></p>
128
+
117
129
  ```text
118
130
  $ crewforge ask kestrel.md -q "How many sensors does Project Kestrel use, and who leads it?" --verify --export session.md
119
131
  ✓ ingested kestrel.md (1 chunks)
@@ -185,6 +197,11 @@ Indonesian forms such as *karena* or *kualitas*). Every step is recorded
185
197
  (file names and hashes, never content or keys) so `disclose` can write the
186
198
  AI-use statement many universities now ask for.
187
199
 
200
+ <p align="center">
201
+ <img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/demo/academic.gif" alt="Terminal demo: crewforge academic feedback --language ms comments in Malay on a student's essay draft, quoting the sentences it refers to, then a no-AI citation check lists (Lim, 2024) as cited in the text but missing from the reference list" width="900">
202
+ </p>
203
+ <p align="center"><sub>A real run of <code>crewforge academic feedback --language ms</code> on a short essay draft in Malay. Each comment quotes the student's own sentence and suggests a fix; nothing is rewritten. The citation check after it uses no AI: it finds <em>(Lim, 2024)</em> cited in the text but missing from the reference list. Only the <code>argument</code> reviewer is run so the result fits on one screen. Recorded with <a href="https://github.com/charmbracelet/vhs">VHS</a> from <a href="https://github.com/aimanmalib/crewforge/blob/main/docs/demo/academic.tape"><code>docs/demo/academic.tape</code></a> (draft, brief and reference are invented) using Gemini 3.8 Flash through an OpenAI-compatible gateway (the <code>custom</code> provider). The wait for the model is shown in full.</sub></p>
204
+
188
205
  ```text
189
206
  $ crewforge academic plan brief.md -f markdown # a 1,200-word TSL3013 essay brief
190
207
  # Assignment plan: Regulated Integration over Prohibition: ...
@@ -221,13 +238,13 @@ Saya menggunakan crewforge academic (versi 0.1.0), sebuah alat berbantukan AI, d
221
238
  - 22 Oktober 2026: Perancangan: alat ini menukar taklimat tugasan (brief-ms.md) kepada 3 soalan kajian, rangka 5 bahagian dan peruntukan perkataan. ...
222
239
  ```
223
240
 
224
- How sources are labelled — there is no "verified" status, because the tool
241
+ How sources are labelled: there is no "verified" status, because the tool
225
242
  cannot check that a source exists:
226
243
 
227
244
  | Source | Label |
228
245
  |---|---|
229
246
  | You supplied it | *supplied by you (not checked online)* |
230
- | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED** — check it |
247
+ | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED**: check it |
231
248
  | The model proposed it with none of those | **LIKELY FABRICATED** |
232
249
  | The model added a DOI/URL/ISBN to a reference you supplied | **UNVERIFIED**, naming the added identifier |
233
250
 
@@ -262,47 +279,20 @@ An unknown provider name is a configuration error, not a silent fallback.
262
279
 
263
280
  ## Architecture
264
281
 
265
- ```mermaid
266
- flowchart LR
267
- CLI["crewforge CLI<br/>review · ask · summarize · ingest<br/>scaffold · validate · write<br/>academic plan · feedback · cite · disclose"] --> R
268
-
269
- subgraph R["Recipes (src/crewforge/recipes)"]
270
- RV[review]
271
- DC[docs]
272
- AP[api]
273
- CT[content]
274
- AC[academic]
275
- end
276
-
277
- R --> CREW
278
-
279
- subgraph CORE["Core (src/crewforge/core)"]
280
- CREW["Crew<br/>step() · parallel()<br/>names failed agents"]
281
- AG["Agent<br/>ask() · ask_json()<br/>rejects empty output"]
282
- PA["parsing<br/>tolerant JSON + schema"]
283
- CL["LLMClient<br/>stream=false · retries 429/5xx only"]
284
- US["UsageTracker<br/>provider-reported tokens"]
285
- CF["LLMConfig + provider presets<br/>keys from env only"]
286
- CREW --> AG
287
- AG --> PA
288
- AG --> CL
289
- AG --> US
290
- CL --> CF
291
- end
292
-
293
- CL -->|"/chat/completions"| P[("Any OpenAI-compatible provider")]
294
- ```
282
+ <p align="center"><img src="https://raw.githubusercontent.com/aimanmalib/crewforge/main/docs/architecture.png" alt="Architecture: the CLI dispatches to five recipes; each recipe builds a Crew of Agents on the shared core (parsing, LLMClient, LLMConfig, UsageTracker), which talks to any OpenAI-compatible provider over /chat/completions" width="100%"></p>
283
+
284
+ <sub>Source: [docs/architecture.mmd](https://github.com/aimanmalib/crewforge/blob/main/docs/architecture.mmd) (re-render with `scripts/render_architecture.py`).</sub>
295
285
 
296
- - **`core/client.py`** — one async client. It always sends `"stream": false`,
286
+ - **`core/client.py`**: one async client. It always sends `"stream": false`,
297
287
  retries only HTTP 429, 5xx and network/timeout errors (with capped exponential
298
288
  backoff that honours `Retry-After`), and fails on the first 401/403/4xx with a
299
289
  message naming the variable to fix. The key is only ever a request header.
300
- - **`core/agent.py`** — an agent is a role, a system prompt and per-agent
290
+ - **`core/agent.py`**: an agent is a role, a system prompt and per-agent
301
291
  sampling defaults. `ask()` records usage and raises if the reply is blank;
302
292
  `ask_json()` additionally requires parseable JSON (optionally a pydantic schema).
303
- - **`core/crew.py`** — runs agents as sequential steps or parallel stages and
293
+ - **`core/crew.py`**: runs agents as sequential steps or parallel stages and
304
294
  turns any failure into a `CrewFailedError` that lists every failed agent.
305
- - **`core/usage.py`** — per-agent calls, tokens, retries and latency, taken from
295
+ - **`core/usage.py`**: per-agent calls, tokens, retries and latency, taken from
306
296
  the provider's `usage` object. Calls without usage are counted as "not reported",
307
297
  never estimated.
308
298
 
@@ -367,22 +357,23 @@ watching them fail; see [docs/porting.md](https://github.com/aimanmalib/crewforg
367
357
 
368
358
  crewforge was built with AI coding assistance (Claude Code) under the
369
359
  direction of its author, Muhammad Aiman. It consolidates four earlier tools by
370
- the same author — [codeguard](https://github.com/aimanmalib/codeguard),
360
+ the same author ([codeguard](https://github.com/aimanmalib/codeguard),
371
361
  [documind](https://github.com/aimanmalib/documind),
372
362
  [apiforge](https://github.com/aimanmalib/apiforge) and
373
- [contentforge](https://github.com/aimanmalib/contentforge) — whose test suites
363
+ [contentforge](https://github.com/aimanmalib/contentforge)), whose test suites
374
364
  were carried over into this repository (see [docs/porting.md](https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md)
375
365
  for what was ported, re-targeted at the shared core, or dropped and why).
376
366
 
377
367
  The evidence that it works is meant to be checkable: the test suite (run
378
- `pytest`; it needs no key and no network), CI on Python 3.10–3.13, and the
368
+ `pytest`; it needs no key and no network), CI on Python 3.10–3.14, and the
379
369
  real-provider runs shown above, two of which found bugs that were fixed with
380
370
  tests before the output was recorded.
381
371
 
382
372
  ## Contributing
383
373
 
384
- See [CONTRIBUTING.md](https://github.com/aimanmalib/crewforge/blob/main/CONTRIBUTING.md). In short: `pip install -e ".[dev,docs]"`,
385
- `pytest`, `ruff check . && ruff format --check .`, and every new failure path
374
+ See [CONTRIBUTING.md](https://github.com/aimanmalib/crewforge/blob/main/CONTRIBUTING.md). In short: `uv sync --extra dev --extra docs`
375
+ (versions from the committed `uv.lock`, as in CI), `uv run pytest`,
376
+ `uv run ruff check . && uv run ruff format --check .`, and every new failure path
386
377
  needs a test that fails if the guard is removed.
387
378
 
388
379
  ## Security
@@ -392,10 +383,52 @@ Please report vulnerabilities privately through
392
383
  See [SECURITY.md](https://github.com/aimanmalib/crewforge/blob/main/SECURITY.md) for scope notes (what is sent to your provider,
393
384
  and what `validate` executes).
394
385
 
395
- ## License
386
+ ## Bahasa Melayu
387
+
388
+ crewforge ialah alat baris arahan dan pustaka Python yang menjalankan satu kru ejen LLM melalui lima resipi:
389
+ semakan kod (`review`), soal jawab dokumen (`ask`, `summarize`), penjanaan projek API FastAPI (`scaffold`,
390
+ `validate`), penulisan kandungan (`write`) dan bantuan penulisan akademik yang jujur (`academic`). Ia berfungsi
391
+ dengan mana-mana penyedia serasi OpenAI, termasuk model tempatan melalui Ollama atau llama.cpp. Kunci API hanya
392
+ dibaca dari pemboleh ubah persekitaran, tidak pernah ditulis ke fail.
393
+
394
+ **Mengapa satu kru.** crewforge menggabungkan empat alat lama (codeguard, documind, apiforge, contentforge) yang
395
+ masing-masing menyimpan salinan sendiri bagi rangka yang sama. Salinan itu sudah berbeza dan setiap satu
396
+ menyembunyikan kegagalan dengan cara sendiri. Kini teras dikongsi: pembaikan dibuat sekali, dan setiap alat menjadi
397
+ resipi di atas teras itu.
398
+
399
+ **Gagal dengan jelas.** Larian yang ejennya memulangkan jawapan kosong atau tidak boleh dihurai, atau pengesah
400
+ yang tidak menyemak apa-apa, keluar dengan kod 1 dan menamakan ejen itu. Kod keluar: `0` berjaya, `1` larian
401
+ gagal, `2` ralat penggunaan atau konfigurasi. Bilangan token diambil daripada laporan penyedia sahaja, tidak
402
+ dianggar.
403
+
404
+ **Resipi `academic`.** Membantu pelajar dan penyelidik dengan kerja mereka **sendiri**: merancang dari taklimat
405
+ tugasan, memberi ulasan atas draf tanpa menulis semula, memformat rujukan yang diberi pengguna (APA 7, IEEE,
406
+ Harvard) tanpa menambah rujukan baharu, dan menulis pernyataan penggunaan AI daripada rekod langkah yang
407
+ dijalankan. Output dalam bahasa Inggeris atau Bahasa Melayu (`--language ms`); output BM disemak untuk bentuk
408
+ Indonesia seperti *karena* atau *kualitas*. Sumber yang dicadangkan model dilabel UNVERIFIED, atau LIKELY
409
+ FABRICATED jika tiada DOI, URL atau ISBN. Tiada ciri "humanise" atau mengelak pengesan AI.
410
+
411
+ **Pasang dan guna.**
412
+
413
+ ```bash
414
+ pip install crewforge # Python 3.10 hingga 3.14
415
+ pip install "crewforge[docs,api]" # pilihan: PDF/DOCX dan semakan aplikasi FastAPI
416
+ export OPENAI_API_KEY=sk-... # atau penyedia lain, lihat `crewforge providers`
417
+ crewforge review src/
418
+ crewforge academic feedback draf.md --brief taklimat.md --language ms
419
+ ```
420
+
421
+ Senarai penyedia, contoh setiap resipi dan seni bina ada dalam bahagian bahasa Inggeris di atas
422
+ ([Providers](https://github.com/aimanmalib/crewforge#providers), [Recipes](https://github.com/aimanmalib/crewforge#recipes)).
423
+
424
+ ## Licence
425
+
426
+ [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman. Made by [Aiman Malib](https://aimanmalib.com), Malaysia.
427
+
428
+ ## Work with Sasa Teguh
396
429
 
397
- [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman
430
+ [![Work with Sasa Teguh: book a call](https://raw.githubusercontent.com/sasateguh/.github/main/assets/work-with-sasa-teguh.png)](https://sasateguh.com/book)
398
431
 
399
- ---
432
+ I built this project. I run [Sasa Teguh](https://sasateguh.com), a studio in Malaysia that builds websites found on Google and in AI answers, and sets up systems like this one for businesses.
400
433
 
401
- Built by [Sasa Teguh](https://sasateguh.com) · Malaysia
434
+ [Book a call](https://sasateguh.com/book) · hello@sasateguh.com · [More from Sasa Teguh](https://github.com/sasateguh)
@@ -4,18 +4,36 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "crewforge"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "One crew of LLM agents, five recipes: code review, document Q&A, API scaffolding, content and honest academic-writing help — on any OpenAI-compatible provider."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
12
12
  requires-python = ">=3.10"
13
13
  authors = [{ name = "Muhammad Aiman" }]
14
- keywords = ["llm", "agents", "multi-agent", "openai", "code-review", "rag", "fastapi", "content-generation", "academic-writing", "cli"]
14
+ keywords = [
15
+ "llm",
16
+ "agents",
17
+ "multi-agent",
18
+ "ai-agents",
19
+ "openai",
20
+ "openai-compatible",
21
+ "code-review",
22
+ "rag",
23
+ "document-qa",
24
+ "fastapi",
25
+ "api-scaffolding",
26
+ "content-generation",
27
+ "academic-writing",
28
+ "cli",
29
+ ]
15
30
  classifiers = [
16
31
  "Development Status :: 3 - Alpha",
17
32
  "Environment :: Console",
18
33
  "Intended Audience :: Developers",
34
+ "Intended Audience :: Education",
35
+ "Natural Language :: English",
36
+ "Natural Language :: Malay",
19
37
  "Operating System :: OS Independent",
20
38
  "Programming Language :: Python :: 3",
21
39
  "Programming Language :: Python :: 3 :: Only",
@@ -23,8 +41,12 @@ classifiers = [
23
41
  "Programming Language :: Python :: 3.11",
24
42
  "Programming Language :: Python :: 3.12",
25
43
  "Programming Language :: Python :: 3.13",
44
+ "Programming Language :: Python :: 3.14",
45
+ "Topic :: Education",
26
46
  "Topic :: Scientific/Engineering :: Artificial Intelligence",
47
+ "Topic :: Software Development :: Code Generators",
27
48
  "Topic :: Software Development :: Quality Assurance",
49
+ "Topic :: Text Processing :: General",
28
50
  "Typing :: Typed",
29
51
  ]
30
52
  dependencies = [
@@ -44,15 +66,19 @@ dev = [
44
66
  "pytest-asyncio>=0.23",
45
67
  "pytest-cov>=5.0",
46
68
  "ruff>=0.6",
69
+ "mypy>=1.10",
70
+ "types-PyYAML>=6.0",
47
71
  "fastapi>=0.110",
48
72
  "httpx>=0.27",
49
73
  ]
50
74
 
51
75
  [project.urls]
52
76
  Homepage = "https://github.com/aimanmalib/crewforge"
53
- Repository = "https://github.com/aimanmalib/crewforge"
54
- Issues = "https://github.com/aimanmalib/crewforge/issues"
77
+ Documentation = "https://github.com/aimanmalib/crewforge#readme"
55
78
  Changelog = "https://github.com/aimanmalib/crewforge/blob/main/CHANGELOG.md"
79
+ Issues = "https://github.com/aimanmalib/crewforge/issues"
80
+ Source = "https://github.com/aimanmalib/crewforge"
81
+ Security = "https://github.com/aimanmalib/crewforge/security/policy"
56
82
 
57
83
  [project.scripts]
58
84
  crewforge = "crewforge.cli:main"
@@ -80,6 +106,22 @@ select = ["E", "F", "W", "I", "UP", "B"]
80
106
  [tool.ruff.lint.per-file-ignores]
81
107
  "tests/**" = ["B011"]
82
108
 
109
+ [tool.mypy]
110
+ # CI runs `mypy` (lint job). Bodies of unannotated functions are checked too.
111
+ files = ["src/crewforge"]
112
+ python_version = "3.10"
113
+ plugins = ["pydantic.mypy"]
114
+ check_untyped_defs = true
115
+ strict_equality = true
116
+ warn_redundant_casts = true
117
+ warn_unused_ignores = true
118
+ warn_unused_configs = true
119
+
120
+ [[tool.mypy.overrides]]
121
+ # Pre-1.24 PyMuPDF import name, used only as a fallback; it ships no type information.
122
+ module = ["fitz"]
123
+ ignore_missing_imports = true
124
+
83
125
  [tool.coverage.run]
84
126
  source = ["crewforge"]
85
127
  branch = true
@@ -1,3 +1,3 @@
1
1
  """crewforge — one crew of LLM agents, five recipes, any OpenAI-compatible provider."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -66,12 +66,13 @@ class Identifiers:
66
66
  def identifiers(text: str) -> Identifiers:
67
67
  """Find the identifiers in a reference. URLs pointing at doi.org count as the DOI."""
68
68
  dois = {_strip_trailing(m.group(1)).lower() for m in _DOI.finditer(text)}
69
- urls = set()
69
+ urls: set[str] = set()
70
70
  for m in _URL.finditer(text):
71
71
  url = _strip_trailing(m.group(0)).rstrip("/")
72
72
  if not any(host in url.lower() for host in _DOI_HOSTS):
73
73
  urls.add(url)
74
- valid, invalid = set(), set()
74
+ valid: set[str] = set()
75
+ invalid: set[str] = set()
75
76
  # Springer-style DOIs embed an ISBN ("10.1007/978-3-..."): search the rest only.
76
77
  rest = _URL.sub(" ", _DOI.sub(" ", text))
77
78
  raw_isbns = [m.group(1) for m in _ISBN_LABELLED.finditer(rest)]