crewforge 0.2.1__tar.gz → 0.3.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 (80) hide show
  1. {crewforge-0.2.1/src/crewforge.egg-info → crewforge-0.3.0}/PKG-INFO +99 -23
  2. {crewforge-0.2.1 → crewforge-0.3.0}/README.md +98 -22
  3. {crewforge-0.2.1 → crewforge-0.3.0}/pyproject.toml +1 -1
  4. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/__init__.py +1 -1
  5. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/cli.py +21 -6
  6. crewforge-0.3.0/src/crewforge/recipes/review/report.py +191 -0
  7. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/sources.py +33 -3
  8. {crewforge-0.2.1 → crewforge-0.3.0/src/crewforge.egg-info}/PKG-INFO +99 -23
  9. crewforge-0.2.1/src/crewforge/recipes/review/report.py +0 -103
  10. {crewforge-0.2.1 → crewforge-0.3.0}/LICENSE +0 -0
  11. {crewforge-0.2.1 → crewforge-0.3.0}/setup.cfg +0 -0
  12. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/__main__.py +0 -0
  13. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/cli.py +0 -0
  14. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/cli_support.py +0 -0
  15. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/__init__.py +0 -0
  16. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/agent.py +0 -0
  17. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/client.py +0 -0
  18. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/config.py +0 -0
  19. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/crew.py +0 -0
  20. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/errors.py +0 -0
  21. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/parsing.py +0 -0
  22. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/providers.py +0 -0
  23. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/usage.py +0 -0
  24. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/py.typed +0 -0
  25. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/__init__.py +0 -0
  26. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/__init__.py +0 -0
  27. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/agents.py +0 -0
  28. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/citations.py +0 -0
  29. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/cli.py +0 -0
  30. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/disclosure.py +0 -0
  31. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/language.py +0 -0
  32. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/locales/en.yaml +0 -0
  33. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/locales/ms.yaml +0 -0
  34. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/pipeline.py +0 -0
  35. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/report.py +0 -0
  36. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/settings.py +0 -0
  37. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/__init__.py +0 -0
  38. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/agents.py +0 -0
  39. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/analytics.py +0 -0
  40. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/appcheck.py +0 -0
  41. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/cli.py +0 -0
  42. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/formatters.py +0 -0
  43. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/generator.py +0 -0
  44. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/pipeline.py +0 -0
  45. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/scaffold.py +0 -0
  46. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/docker-compose.j2 +0 -0
  47. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/dockerfile.j2 +0 -0
  48. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/env.example.j2 +0 -0
  49. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/fastapi_main.j2 +0 -0
  50. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/model.j2 +0 -0
  51. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/requirements.j2 +0 -0
  52. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/route.j2 +0 -0
  53. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/test.j2 +0 -0
  54. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/utils.py +0 -0
  55. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/__init__.py +0 -0
  56. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/agents.py +0 -0
  57. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/cli.py +0 -0
  58. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/export.py +0 -0
  59. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/pipeline.py +0 -0
  60. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/settings.py +0 -0
  61. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/text.py +0 -0
  62. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/__init__.py +0 -0
  63. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/agents.py +0 -0
  64. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/cli.py +0 -0
  65. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/export.py +0 -0
  66. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/index.py +0 -0
  67. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/ingest.py +0 -0
  68. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/pipeline.py +0 -0
  69. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/retrieve.py +0 -0
  70. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/settings.py +0 -0
  71. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/__init__.py +0 -0
  72. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/agents.py +0 -0
  73. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/pipeline.py +0 -0
  74. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/SOURCES.txt +0 -0
  75. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/dependency_links.txt +0 -0
  76. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/entry_points.txt +0 -0
  77. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/requires.txt +0 -0
  78. {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/top_level.txt +0 -0
  79. {crewforge-0.2.1 → crewforge-0.3.0}/tests/test_honest_failure_e2e.py +0 -0
  80. {crewforge-0.2.1 → crewforge-0.3.0}/tests/test_readme.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crewforge
3
- Version: 0.2.1
3
+ Version: 0.3.0
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
@@ -56,15 +56,21 @@ Requires-Dist: fastapi>=0.110; extra == "dev"
56
56
  Requires-Dist: httpx>=0.27; extra == "dev"
57
57
  Dynamic: license-file
58
58
 
59
- # 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
60
 
61
- **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.**
61
+ <h1 align="center">crewforge</h1>
62
62
 
63
- [![CI](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml/badge.svg)](https://github.com/aimanmalib/crewforge/actions/workflows/ci.yml)
64
- [![PyPI](https://img.shields.io/pypi/v/crewforge)](https://pypi.org/project/crewforge/)
65
- [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/aimanmalib/crewforge/badge)](https://scorecard.dev/viewer/?uri=github.com/aimanmalib/crewforge)
66
- ![Python 3.10–3.14](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue)
67
- ![License: MIT](https://img.shields.io/badge/license-MIT-green)
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>
64
+
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>
67
+
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)
68
74
 
69
75
  ```bash
70
76
  crewforge review src/ # six reviewers + a summary
@@ -81,7 +87,7 @@ crewforge academic feedback draft.md --brief brief.md --language ms
81
87
 
82
88
  ## Why one crew, many recipes
83
89
 
84
- crewforge started as four separate command-line tools —
90
+ crewforge started as four separate command-line tools:
85
91
  [codeguard](https://github.com/aimanmalib/codeguard),
86
92
  [documind](https://github.com/aimanmalib/documind),
87
93
  [apiforge](https://github.com/aimanmalib/apiforge) and
@@ -95,7 +101,7 @@ showed the copies had drifted and that each hid its failures differently:
95
101
  - apiforge scored a spec with **no endpoints** 100/100;
96
102
  - contentforge wrote the real API key into its YAML file, and sent `${OPENAI_API_KEY}` literally as the key.
97
103
 
98
- 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
99
105
  *recipe*: a set of agents plus the steps that connect them. The same core rules
100
106
  apply to every recipe: non-streaming requests, bounded retries, usage numbers
101
107
  taken only from the provider, keys only from the environment, and no run that
@@ -139,7 +145,7 @@ tables are trimmed to their totals.
139
145
 
140
146
  | Command | Agents | What you get |
141
147
  |---|---|---|
142
- | `review PATH` | security, style, complexity, logic, testing, documentation (parallel) → summary | Findings by severity, a decision and a score; `-f json` for CI |
148
+ | `review PATH` | security, style, complexity, logic, testing, documentation (parallel) → summary | Findings by severity, a decision and a score; `-f json` for CI, `-f sarif` for GitHub code scanning |
143
149
  | `ingest FILES` | ingester, indexer (no LLM) | How each file was parsed and chunked |
144
150
  | `ask FILES -q Q [--verify] [--rerank]` | retriever → answerer → fact-checker | An answer with `[Source N]` citations and a claim-by-claim verdict |
145
151
  | `summarize FILES` | summarizer | Summary and key points per document |
@@ -171,6 +177,30 @@ $ crewforge review app.py -f markdown -o review.md # app.py builds SQL with
171
177
  total 7 calls 22,366 tokens (provider-reported)
172
178
  ```
173
179
 
180
+ `-f sarif` writes a [Static Analysis Results Interchange Format 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html)
181
+ log (`sarif`), the format GitHub code scanning reads, so findings can appear in a repository's Security tab. Each finding becomes one
182
+ result: `critical` and `error` map to level `error`, `warning` to `warning`, `info` to `note`; the rule is the
183
+ finding's CWE when it has one, otherwise the reviewer's name. When you review a directory, the line a reviewer
184
+ reports is mapped back to the file it came from. A finding whose line is missing or outside the code points at the
185
+ reviewed path without a line. Reviewers that fail are listed in `invocations`, and the exit code is the same as for
186
+ the other formats.
187
+
188
+ ```yaml
189
+ # .github/workflows/review.yml (needs an API key secret, e.g. OPENAI_API_KEY)
190
+ permissions:
191
+ contents: read
192
+ security-events: write
193
+ steps:
194
+ - uses: actions/checkout@v4
195
+ - run: pipx run crewforge review src/ -f sarif -o crewforge.sarif
196
+ env:
197
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
198
+ - uses: github/codeql-action/upload-sarif@v3
199
+ if: always()
200
+ with:
201
+ sarif_file: crewforge.sarif
202
+ ```
203
+
174
204
  ### `ask` and `summarize`
175
205
 
176
206
  <p align="center">
@@ -290,13 +320,13 @@ Saya menggunakan crewforge academic (versi 0.1.0), sebuah alat berbantukan AI, d
290
320
  - 22 Oktober 2026: Perancangan: alat ini menukar taklimat tugasan (brief-ms.md) kepada 3 soalan kajian, rangka 5 bahagian dan peruntukan perkataan. ...
291
321
  ```
292
322
 
293
- How sources are labelled — there is no "verified" status, because the tool
323
+ How sources are labelled: there is no "verified" status, because the tool
294
324
  cannot check that a source exists:
295
325
 
296
326
  | Source | Label |
297
327
  |---|---|
298
328
  | You supplied it | *supplied by you (not checked online)* |
299
- | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED** — check it |
329
+ | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED**: check it |
300
330
  | The model proposed it with none of those | **LIKELY FABRICATED** |
301
331
  | The model added a DOI/URL/ISBN to a reference you supplied | **UNVERIFIED**, naming the added identifier |
302
332
 
@@ -335,16 +365,16 @@ An unknown provider name is a configuration error, not a silent fallback.
335
365
 
336
366
  <sub>Source: [docs/architecture.mmd](https://github.com/aimanmalib/crewforge/blob/main/docs/architecture.mmd) (re-render with `scripts/render_architecture.py`).</sub>
337
367
 
338
- - **`core/client.py`** — one async client. It always sends `"stream": false`,
368
+ - **`core/client.py`**: one async client. It always sends `"stream": false`,
339
369
  retries only HTTP 429, 5xx and network/timeout errors (with capped exponential
340
370
  backoff that honours `Retry-After`), and fails on the first 401/403/4xx with a
341
371
  message naming the variable to fix. The key is only ever a request header.
342
- - **`core/agent.py`** — an agent is a role, a system prompt and per-agent
372
+ - **`core/agent.py`**: an agent is a role, a system prompt and per-agent
343
373
  sampling defaults. `ask()` records usage and raises if the reply is blank;
344
374
  `ask_json()` additionally requires parseable JSON (optionally a pydantic schema).
345
- - **`core/crew.py`** — runs agents as sequential steps or parallel stages and
375
+ - **`core/crew.py`**: runs agents as sequential steps or parallel stages and
346
376
  turns any failure into a `CrewFailedError` that lists every failed agent.
347
- - **`core/usage.py`** — per-agent calls, tokens, retries and latency, taken from
377
+ - **`core/usage.py`**: per-agent calls, tokens, retries and latency, taken from
348
378
  the provider's `usage` object. Calls without usage are counted as "not reported",
349
379
  never estimated.
350
380
 
@@ -409,10 +439,10 @@ watching them fail; see [docs/porting.md](https://github.com/aimanmalib/crewforg
409
439
 
410
440
  crewforge was built with AI coding assistance (Claude Code) under the
411
441
  direction of its author, Muhammad Aiman. It consolidates four earlier tools by
412
- the same author — [codeguard](https://github.com/aimanmalib/codeguard),
442
+ the same author ([codeguard](https://github.com/aimanmalib/codeguard),
413
443
  [documind](https://github.com/aimanmalib/documind),
414
444
  [apiforge](https://github.com/aimanmalib/apiforge) and
415
- [contentforge](https://github.com/aimanmalib/contentforge) — whose test suites
445
+ [contentforge](https://github.com/aimanmalib/contentforge)), whose test suites
416
446
  were carried over into this repository (see [docs/porting.md](https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md)
417
447
  for what was ported, re-targeted at the shared core, or dropped and why).
418
448
 
@@ -435,10 +465,56 @@ Please report vulnerabilities privately through
435
465
  See [SECURITY.md](https://github.com/aimanmalib/crewforge/blob/main/SECURITY.md) for scope notes (what is sent to your provider,
436
466
  and what `validate` executes).
437
467
 
438
- ## License
468
+ ## Bahasa Melayu
469
+
470
+ crewforge ialah alat baris arahan dan pustaka Python yang menjalankan satu kru ejen LLM melalui lima resipi:
471
+ semakan kod (`review`), soal jawab dokumen (`ask`, `summarize`), penjanaan projek API FastAPI (`scaffold`,
472
+ `validate`), penulisan kandungan (`write`) dan bantuan penulisan akademik yang jujur (`academic`). Ia berfungsi
473
+ dengan mana-mana penyedia serasi OpenAI, termasuk model tempatan melalui Ollama atau llama.cpp. Kunci API hanya
474
+ dibaca dari pemboleh ubah persekitaran, tidak pernah ditulis ke fail.
475
+
476
+ **Mengapa satu kru.** crewforge menggabungkan empat alat lama (codeguard, documind, apiforge, contentforge) yang
477
+ masing-masing menyimpan salinan sendiri bagi rangka yang sama. Salinan itu sudah berbeza dan setiap satu
478
+ menyembunyikan kegagalan dengan cara sendiri. Kini teras dikongsi: pembaikan dibuat sekali, dan setiap alat menjadi
479
+ resipi di atas teras itu.
480
+
481
+ **Gagal dengan jelas.** Larian yang ejennya memulangkan jawapan kosong atau tidak boleh dihurai, atau pengesah
482
+ yang tidak menyemak apa-apa, keluar dengan kod 1 dan menamakan ejen itu. Kod keluar: `0` berjaya, `1` larian
483
+ gagal, `2` ralat penggunaan atau konfigurasi. Bilangan token diambil daripada laporan penyedia sahaja, tidak
484
+ dianggar.
485
+
486
+ **Resipi `academic`.** Membantu pelajar dan penyelidik dengan kerja mereka **sendiri**: merancang dari taklimat
487
+ tugasan, memberi ulasan atas draf tanpa menulis semula, memformat rujukan yang diberi pengguna (APA 7, IEEE,
488
+ Harvard) tanpa menambah rujukan baharu, dan menulis pernyataan penggunaan AI daripada rekod langkah yang
489
+ dijalankan. Output dalam bahasa Inggeris atau Bahasa Melayu (`--language ms`); output BM disemak untuk bentuk
490
+ Indonesia seperti *karena* atau *kualitas*. Sumber yang dicadangkan model dilabel UNVERIFIED, atau LIKELY
491
+ FABRICATED jika tiada DOI, URL atau ISBN. Tiada ciri "humanise" atau mengelak pengesan AI.
492
+
493
+ **Pasang dan guna.**
494
+
495
+ ```bash
496
+ pip install crewforge # Python 3.10 hingga 3.14
497
+ pip install "crewforge[docs,api]" # pilihan: PDF/DOCX dan semakan aplikasi FastAPI
498
+ export OPENAI_API_KEY=sk-... # atau penyedia lain, lihat `crewforge providers`
499
+ crewforge review src/
500
+ crewforge review src/ -f sarif -o crewforge.sarif # untuk GitHub code scanning
501
+ crewforge academic feedback draf.md --brief taklimat.md --language ms
502
+ ```
503
+
504
+ `review -f sarif` menulis laporan `SARIF` 2.1.0 yang dibaca GitHub code scanning: setiap dapatan menjadi satu
505
+ keputusan dalam tab Security, dan baris dalam semakan folder dipetakan kembali ke fail asalnya.
506
+
507
+ Senarai penyedia, contoh setiap resipi dan seni bina ada dalam bahagian bahasa Inggeris di atas
508
+ ([Providers](https://github.com/aimanmalib/crewforge#providers), [Recipes](https://github.com/aimanmalib/crewforge#recipes)).
509
+
510
+ ## Licence
511
+
512
+ [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman. Made by [Aiman Malib](https://aimanmalib.com), Malaysia.
513
+
514
+ ## Work with Sasa Teguh
439
515
 
440
- [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman
516
+ [![Work with Sasa Teguh: book a call](https://raw.githubusercontent.com/sasateguh/.github/main/assets/work-with-sasa-teguh.png)](https://sasateguh.com/book)
441
517
 
442
- ---
518
+ 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.
443
519
 
444
- Built by [Sasa Teguh](https://sasateguh.com) · Malaysia
520
+ [Book a call](https://sasateguh.com/book) · hello@sasateguh.com · [More from Sasa Teguh](https://github.com/sasateguh)
@@ -1,12 +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
- [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/aimanmalib/crewforge/badge)](https://scorecard.dev/viewer/?uri=github.com/aimanmalib/crewforge)
8
- ![Python 3.10–3.14](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue)
9
- ![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)
10
16
 
11
17
  ```bash
12
18
  crewforge review src/ # six reviewers + a summary
@@ -23,7 +29,7 @@ crewforge academic feedback draft.md --brief brief.md --language ms
23
29
 
24
30
  ## Why one crew, many recipes
25
31
 
26
- crewforge started as four separate command-line tools —
32
+ crewforge started as four separate command-line tools:
27
33
  [codeguard](https://github.com/aimanmalib/codeguard),
28
34
  [documind](https://github.com/aimanmalib/documind),
29
35
  [apiforge](https://github.com/aimanmalib/apiforge) and
@@ -37,7 +43,7 @@ showed the copies had drifted and that each hid its failures differently:
37
43
  - apiforge scored a spec with **no endpoints** 100/100;
38
44
  - contentforge wrote the real API key into its YAML file, and sent `${OPENAI_API_KEY}` literally as the key.
39
45
 
40
- 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
41
47
  *recipe*: a set of agents plus the steps that connect them. The same core rules
42
48
  apply to every recipe: non-streaming requests, bounded retries, usage numbers
43
49
  taken only from the provider, keys only from the environment, and no run that
@@ -81,7 +87,7 @@ tables are trimmed to their totals.
81
87
 
82
88
  | Command | Agents | What you get |
83
89
  |---|---|---|
84
- | `review PATH` | security, style, complexity, logic, testing, documentation (parallel) → summary | Findings by severity, a decision and a score; `-f json` for CI |
90
+ | `review PATH` | security, style, complexity, logic, testing, documentation (parallel) → summary | Findings by severity, a decision and a score; `-f json` for CI, `-f sarif` for GitHub code scanning |
85
91
  | `ingest FILES` | ingester, indexer (no LLM) | How each file was parsed and chunked |
86
92
  | `ask FILES -q Q [--verify] [--rerank]` | retriever → answerer → fact-checker | An answer with `[Source N]` citations and a claim-by-claim verdict |
87
93
  | `summarize FILES` | summarizer | Summary and key points per document |
@@ -113,6 +119,30 @@ $ crewforge review app.py -f markdown -o review.md # app.py builds SQL with
113
119
  total 7 calls 22,366 tokens (provider-reported)
114
120
  ```
115
121
 
122
+ `-f sarif` writes a [Static Analysis Results Interchange Format 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html)
123
+ log (`sarif`), the format GitHub code scanning reads, so findings can appear in a repository's Security tab. Each finding becomes one
124
+ result: `critical` and `error` map to level `error`, `warning` to `warning`, `info` to `note`; the rule is the
125
+ finding's CWE when it has one, otherwise the reviewer's name. When you review a directory, the line a reviewer
126
+ reports is mapped back to the file it came from. A finding whose line is missing or outside the code points at the
127
+ reviewed path without a line. Reviewers that fail are listed in `invocations`, and the exit code is the same as for
128
+ the other formats.
129
+
130
+ ```yaml
131
+ # .github/workflows/review.yml (needs an API key secret, e.g. OPENAI_API_KEY)
132
+ permissions:
133
+ contents: read
134
+ security-events: write
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+ - run: pipx run crewforge review src/ -f sarif -o crewforge.sarif
138
+ env:
139
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
140
+ - uses: github/codeql-action/upload-sarif@v3
141
+ if: always()
142
+ with:
143
+ sarif_file: crewforge.sarif
144
+ ```
145
+
116
146
  ### `ask` and `summarize`
117
147
 
118
148
  <p align="center">
@@ -232,13 +262,13 @@ Saya menggunakan crewforge academic (versi 0.1.0), sebuah alat berbantukan AI, d
232
262
  - 22 Oktober 2026: Perancangan: alat ini menukar taklimat tugasan (brief-ms.md) kepada 3 soalan kajian, rangka 5 bahagian dan peruntukan perkataan. ...
233
263
  ```
234
264
 
235
- How sources are labelled — there is no "verified" status, because the tool
265
+ How sources are labelled: there is no "verified" status, because the tool
236
266
  cannot check that a source exists:
237
267
 
238
268
  | Source | Label |
239
269
  |---|---|
240
270
  | You supplied it | *supplied by you (not checked online)* |
241
- | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED** — check it |
271
+ | The model proposed it, with a DOI, URL or valid ISBN | **UNVERIFIED**: check it |
242
272
  | The model proposed it with none of those | **LIKELY FABRICATED** |
243
273
  | The model added a DOI/URL/ISBN to a reference you supplied | **UNVERIFIED**, naming the added identifier |
244
274
 
@@ -277,16 +307,16 @@ An unknown provider name is a configuration error, not a silent fallback.
277
307
 
278
308
  <sub>Source: [docs/architecture.mmd](https://github.com/aimanmalib/crewforge/blob/main/docs/architecture.mmd) (re-render with `scripts/render_architecture.py`).</sub>
279
309
 
280
- - **`core/client.py`** — one async client. It always sends `"stream": false`,
310
+ - **`core/client.py`**: one async client. It always sends `"stream": false`,
281
311
  retries only HTTP 429, 5xx and network/timeout errors (with capped exponential
282
312
  backoff that honours `Retry-After`), and fails on the first 401/403/4xx with a
283
313
  message naming the variable to fix. The key is only ever a request header.
284
- - **`core/agent.py`** — an agent is a role, a system prompt and per-agent
314
+ - **`core/agent.py`**: an agent is a role, a system prompt and per-agent
285
315
  sampling defaults. `ask()` records usage and raises if the reply is blank;
286
316
  `ask_json()` additionally requires parseable JSON (optionally a pydantic schema).
287
- - **`core/crew.py`** — runs agents as sequential steps or parallel stages and
317
+ - **`core/crew.py`**: runs agents as sequential steps or parallel stages and
288
318
  turns any failure into a `CrewFailedError` that lists every failed agent.
289
- - **`core/usage.py`** — per-agent calls, tokens, retries and latency, taken from
319
+ - **`core/usage.py`**: per-agent calls, tokens, retries and latency, taken from
290
320
  the provider's `usage` object. Calls without usage are counted as "not reported",
291
321
  never estimated.
292
322
 
@@ -351,10 +381,10 @@ watching them fail; see [docs/porting.md](https://github.com/aimanmalib/crewforg
351
381
 
352
382
  crewforge was built with AI coding assistance (Claude Code) under the
353
383
  direction of its author, Muhammad Aiman. It consolidates four earlier tools by
354
- the same author — [codeguard](https://github.com/aimanmalib/codeguard),
384
+ the same author ([codeguard](https://github.com/aimanmalib/codeguard),
355
385
  [documind](https://github.com/aimanmalib/documind),
356
386
  [apiforge](https://github.com/aimanmalib/apiforge) and
357
- [contentforge](https://github.com/aimanmalib/contentforge) — whose test suites
387
+ [contentforge](https://github.com/aimanmalib/contentforge)), whose test suites
358
388
  were carried over into this repository (see [docs/porting.md](https://github.com/aimanmalib/crewforge/blob/main/docs/porting.md)
359
389
  for what was ported, re-targeted at the shared core, or dropped and why).
360
390
 
@@ -377,10 +407,56 @@ Please report vulnerabilities privately through
377
407
  See [SECURITY.md](https://github.com/aimanmalib/crewforge/blob/main/SECURITY.md) for scope notes (what is sent to your provider,
378
408
  and what `validate` executes).
379
409
 
380
- ## License
410
+ ## Bahasa Melayu
411
+
412
+ crewforge ialah alat baris arahan dan pustaka Python yang menjalankan satu kru ejen LLM melalui lima resipi:
413
+ semakan kod (`review`), soal jawab dokumen (`ask`, `summarize`), penjanaan projek API FastAPI (`scaffold`,
414
+ `validate`), penulisan kandungan (`write`) dan bantuan penulisan akademik yang jujur (`academic`). Ia berfungsi
415
+ dengan mana-mana penyedia serasi OpenAI, termasuk model tempatan melalui Ollama atau llama.cpp. Kunci API hanya
416
+ dibaca dari pemboleh ubah persekitaran, tidak pernah ditulis ke fail.
417
+
418
+ **Mengapa satu kru.** crewforge menggabungkan empat alat lama (codeguard, documind, apiforge, contentforge) yang
419
+ masing-masing menyimpan salinan sendiri bagi rangka yang sama. Salinan itu sudah berbeza dan setiap satu
420
+ menyembunyikan kegagalan dengan cara sendiri. Kini teras dikongsi: pembaikan dibuat sekali, dan setiap alat menjadi
421
+ resipi di atas teras itu.
422
+
423
+ **Gagal dengan jelas.** Larian yang ejennya memulangkan jawapan kosong atau tidak boleh dihurai, atau pengesah
424
+ yang tidak menyemak apa-apa, keluar dengan kod 1 dan menamakan ejen itu. Kod keluar: `0` berjaya, `1` larian
425
+ gagal, `2` ralat penggunaan atau konfigurasi. Bilangan token diambil daripada laporan penyedia sahaja, tidak
426
+ dianggar.
427
+
428
+ **Resipi `academic`.** Membantu pelajar dan penyelidik dengan kerja mereka **sendiri**: merancang dari taklimat
429
+ tugasan, memberi ulasan atas draf tanpa menulis semula, memformat rujukan yang diberi pengguna (APA 7, IEEE,
430
+ Harvard) tanpa menambah rujukan baharu, dan menulis pernyataan penggunaan AI daripada rekod langkah yang
431
+ dijalankan. Output dalam bahasa Inggeris atau Bahasa Melayu (`--language ms`); output BM disemak untuk bentuk
432
+ Indonesia seperti *karena* atau *kualitas*. Sumber yang dicadangkan model dilabel UNVERIFIED, atau LIKELY
433
+ FABRICATED jika tiada DOI, URL atau ISBN. Tiada ciri "humanise" atau mengelak pengesan AI.
434
+
435
+ **Pasang dan guna.**
436
+
437
+ ```bash
438
+ pip install crewforge # Python 3.10 hingga 3.14
439
+ pip install "crewforge[docs,api]" # pilihan: PDF/DOCX dan semakan aplikasi FastAPI
440
+ export OPENAI_API_KEY=sk-... # atau penyedia lain, lihat `crewforge providers`
441
+ crewforge review src/
442
+ crewforge review src/ -f sarif -o crewforge.sarif # untuk GitHub code scanning
443
+ crewforge academic feedback draf.md --brief taklimat.md --language ms
444
+ ```
445
+
446
+ `review -f sarif` menulis laporan `SARIF` 2.1.0 yang dibaca GitHub code scanning: setiap dapatan menjadi satu
447
+ keputusan dalam tab Security, dan baris dalam semakan folder dipetakan kembali ke fail asalnya.
448
+
449
+ Senarai penyedia, contoh setiap resipi dan seni bina ada dalam bahagian bahasa Inggeris di atas
450
+ ([Providers](https://github.com/aimanmalib/crewforge#providers), [Recipes](https://github.com/aimanmalib/crewforge#recipes)).
451
+
452
+ ## Licence
453
+
454
+ [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman. Made by [Aiman Malib](https://aimanmalib.com), Malaysia.
455
+
456
+ ## Work with Sasa Teguh
381
457
 
382
- [MIT](https://github.com/aimanmalib/crewforge/blob/main/LICENSE) © Muhammad Aiman
458
+ [![Work with Sasa Teguh: book a call](https://raw.githubusercontent.com/sasateguh/.github/main/assets/work-with-sasa-teguh.png)](https://sasateguh.com/book)
383
459
 
384
- ---
460
+ 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.
385
461
 
386
- Built by [Sasa Teguh](https://sasateguh.com) · Malaysia
462
+ [Book a call](https://sasateguh.com/book) · hello@sasateguh.com · [More from Sasa Teguh](https://github.com/sasateguh)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "crewforge"
7
- version = "0.2.1"
7
+ version = "0.3.0"
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"
@@ -1,3 +1,3 @@
1
1
  """crewforge — one crew of LLM agents, five recipes, any OpenAI-compatible provider."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.3.0"
@@ -22,8 +22,14 @@ from ...cli_support import (
22
22
  from ...core.config import LLMConfig
23
23
  from .agents import REVIEW_AGENTS
24
24
  from .pipeline import CodeReviewCrew, ReviewResult
25
- from .report import report_dict, report_markdown
26
- from .sources import DEFAULT_MAX_FILE_BYTES, SourceError, collect, detect_language
25
+ from .report import report_dict, report_markdown, report_sarif
26
+ from .sources import (
27
+ DEFAULT_MAX_FILE_BYTES,
28
+ SourceError,
29
+ Sources,
30
+ collect,
31
+ detect_language,
32
+ )
27
33
 
28
34
  # Kept under the old private name: codeguard's tests and users imported it.
29
35
  _detect_language = detect_language
@@ -60,7 +66,8 @@ async def _run_review(
60
66
  "fmt",
61
67
  default="text",
62
68
  show_default=True,
63
- type=click.Choice(["text", "json", "markdown"]),
69
+ type=click.Choice(["text", "json", "markdown", "sarif"]),
70
+ help="sarif = SARIF 2.1.0 for GitHub code scanning.",
64
71
  )
65
72
  @click.option("--output", "-o", default="", help="Write the report to this file.")
66
73
  @click.option(
@@ -77,6 +84,7 @@ def review(ctx, path, path_opt, language, fmt, output, allow_partial):
77
84
  Example:
78
85
  crewforge review app.py
79
86
  crewforge review src/ -f json -o review.json
87
+ crewforge review src/ -f sarif -o review.sarif
80
88
  """
81
89
  target = path or path_opt
82
90
  if not target:
@@ -116,11 +124,18 @@ def review(ctx, path, path_opt, language, fmt, output, allow_partial):
116
124
  allow_partial=allow_partial,
117
125
  )
118
126
  )
119
- _display_results(result, fmt, output)
127
+ _display_results(result, fmt, output, sources, target)
120
128
 
121
129
 
122
- def _display_results(result: ReviewResult, fmt: str, output: str) -> None:
123
- text = json.dumps(report_dict(result), indent=2) if fmt == "json" else report_markdown(result)
130
+ def _display_results(
131
+ result: ReviewResult, fmt: str, output: str, sources: Sources, target: str
132
+ ) -> None:
133
+ if fmt == "json":
134
+ text = json.dumps(report_dict(result), indent=2)
135
+ elif fmt == "sarif":
136
+ text = json.dumps(report_sarif(result, sources, target), indent=2)
137
+ else:
138
+ text = report_markdown(result)
124
139
  if output:
125
140
  Path(output).write_text(text, encoding="utf-8")
126
141
  err_console.print(f"[green]Report written to {output}[/]")