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.
- {crewforge-0.2.1/src/crewforge.egg-info → crewforge-0.3.0}/PKG-INFO +99 -23
- {crewforge-0.2.1 → crewforge-0.3.0}/README.md +98 -22
- {crewforge-0.2.1 → crewforge-0.3.0}/pyproject.toml +1 -1
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/__init__.py +1 -1
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/cli.py +21 -6
- crewforge-0.3.0/src/crewforge/recipes/review/report.py +191 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/sources.py +33 -3
- {crewforge-0.2.1 → crewforge-0.3.0/src/crewforge.egg-info}/PKG-INFO +99 -23
- crewforge-0.2.1/src/crewforge/recipes/review/report.py +0 -103
- {crewforge-0.2.1 → crewforge-0.3.0}/LICENSE +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/setup.cfg +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/__main__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/cli.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/cli_support.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/agent.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/client.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/config.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/crew.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/errors.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/parsing.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/providers.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/core/usage.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/py.typed +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/agents.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/citations.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/cli.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/disclosure.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/language.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/locales/en.yaml +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/locales/ms.yaml +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/pipeline.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/report.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/academic/settings.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/agents.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/analytics.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/appcheck.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/cli.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/formatters.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/generator.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/pipeline.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/scaffold.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/docker-compose.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/dockerfile.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/env.example.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/fastapi_main.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/model.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/requirements.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/route.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/templates/test.j2 +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/api/utils.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/agents.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/cli.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/export.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/pipeline.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/settings.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/content/text.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/agents.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/cli.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/export.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/index.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/ingest.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/pipeline.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/retrieve.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/docs/settings.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/__init__.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/agents.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge/recipes/review/pipeline.py +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/SOURCES.txt +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/dependency_links.txt +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/entry_points.txt +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/requires.txt +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/src/crewforge.egg-info/top_level.txt +0 -0
- {crewforge-0.2.1 → crewforge-0.3.0}/tests/test_honest_failure_e2e.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
61
|
+
<h1 align="center">crewforge</h1>
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
##
|
|
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
|
-
[
|
|
516
|
+
[](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
|
-
|
|
520
|
+
[Book a call](https://sasateguh.com/book) · hello@sasateguh.com · [More from Sasa Teguh](https://github.com/sasateguh)
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
3
|
+
<h1 align="center">crewforge</h1>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
##
|
|
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
|
-
[
|
|
458
|
+
[](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
|
-
|
|
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.
|
|
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"
|
|
@@ -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
|
|
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(
|
|
123
|
-
|
|
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}[/]")
|