Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
run: npm ci

- name: Run unit tests with coverage
run: npm test -- --coverage
run: npm run test:coverage

- name: Upload coverage
if: always()
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

# Roda testes com coverage para gerar relatório
- name: Run unit tests with coverage
run: npm test -- --coverage
run: npm run test:coverage

# Bloqueia se coverage < 80%
- name: Check coverage threshold (>=80%)
Expand Down
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ docs-site/.docusaurus/
# TypeScript
*.tsbuildinfo

# Python
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.venv/
venv/

# Logs
*.log
npm-debug.log*
Expand Down Expand Up @@ -90,6 +98,7 @@ docs/**
!docs/YOOL_TUPLE_HAMT.md
scripts/**
!scripts/build_hamt.py
!scripts/coverage.js
!scripts/skillopt/
!scripts/skillopt/*.js
playwright-report/**
Expand All @@ -98,6 +107,8 @@ tests/**
!tests/unit/*.test.js
!tests/e2e/
!tests/e2e/*.spec.ts
!tests/python/
!tests/python/*.py
test-results/**
coverage/**
bootstrap.ps1
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Format follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) an
## [Unreleased]

### Added
- Standalone Python distribution `simplicio-mapper` on PyPI: dependency-free
`simplicio_mapper.mapper` port of the Node mapper plus a `map` / `update` CLI
exposed as the `simplicio-mapper` and `llm-project-mapper` console scripts.
Generates the same `.simplicio/project-map.json` and `precedent-index.json`
without requiring a Node toolchain.
- `map` / `update` CLI subcommands for generating and incrementally refreshing
`.simplicio/project-map.json` and `.simplicio/precedent-index.json`.
- Rich machine-readable mapper artifacts with file inventory, roles, imports,
Expand Down
72 changes: 72 additions & 0 deletions PYPI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# simplicio-mapper

Python-first project mapper for the Simplicio ecosystem. It scans a repository
and emits two machine-readable artifacts that agents and tooling can consume
without parsing the human-readable markdown docs:

- `.simplicio/project-map.json` (`simplicio.project-map/v1`) — file inventory,
architecture signals, entry points, tests, modules, entities, dependencies
and recent changes.
- `.simplicio/precedent-index.json` (`simplicio.precedent-index/v1`) —
high-signal code examples tagged by change type, file, language, roles and
snippet.

The full contract is documented in
[SIMPLICIO_INTEGRATION.md](https://github.com/wesleysimplicio/simplicio-mapper/blob/main/SIMPLICIO_INTEGRATION.md).

## Install

```bash
pip install simplicio-mapper
```

## Usage

```bash
# Map the current directory into .simplicio/
simplicio-mapper map

# Refresh artifacts and record changed files since the last run
simplicio-mapper update

# Map another project root, with hints when .starter-meta.json is absent
simplicio-mapper map --root path/to/project --stack python --product-name "My App"

# Re-run automatically while files change locally
simplicio-mapper map --watch
```

The `llm-project-mapper` console script is provided as an alias.

### Options

| Option | Description |
|---|---|
| `--root <dir>` | Project root to map. Defaults to the current directory. |
| `--out <dir>` | Artifact directory. Defaults to `.simplicio`. |
| `--stack <name>` | Stack hint when `.starter-meta.json` is absent. |
| `--product-name <name>` | Product name hint when `.starter-meta.json` is absent. |
| `--incremental` | Record changed files and update existing artifacts. |
| `--watch` | Re-run mapping when local files change. |
| `--silent` | Minimal output. |
| `-V`, `--version` | Show version and exit. |
| `-h`, `--help` | Show help. |

## Consuming the artifacts

```python
from pathlib import Path
import json

base = Path(".simplicio")
project_map = json.loads((base / "project-map.json").read_text())
precedents = json.loads((base / "precedent-index.json").read_text())

top_files = sorted(
project_map["files"], key=lambda f: f.get("importance", 0), reverse=True
)[:8]
```

## License

MIT
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ npx @wesleysimplicio/llm-project-mapper map --incremental
npx @wesleysimplicio/llm-project-mapper update
```

### New: standalone Python CLI

The mapper now ships as a dependency-free Python package, so Python-first teams
can generate the same artifacts without a Node toolchain:

```bash
pip install simplicio-mapper

simplicio-mapper map # write .simplicio/ artifacts
simplicio-mapper update # refresh and record changed files
simplicio-mapper map --watch # re-map as files change locally
```

Both `simplicio-mapper` and `llm-project-mapper` console scripts are installed,
and the Python output is byte-for-byte compatible with the Node mapper's schema.

Use `--watch` during long agent sessions to keep the map fresh. The schema and
Python consumption example live in [SIMPLICIO_INTEGRATION.md](SIMPLICIO_INTEGRATION.md).

Expand Down
16 changes: 16 additions & 0 deletions README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ npx @wesleysimplicio/llm-project-mapper map --incremental
npx @wesleysimplicio/llm-project-mapper update
```

### Novidade: CLI Python standalone

O mapper agora tambem e distribuido como pacote Python sem dependencias, para
times Python-first gerarem os mesmos artefatos sem toolchain Node:

```bash
pip install simplicio-mapper

simplicio-mapper map # escreve os artefatos em .simplicio/
simplicio-mapper update # atualiza e registra arquivos alterados
simplicio-mapper map --watch # remapeia conforme arquivos mudam
```

Os console scripts `simplicio-mapper` e `llm-project-mapper` sao instalados, e a
saida Python e compativel com o schema do mapper Node.

Use `--watch` durante sessoes longas de agentes para manter o mapa fresco. O
schema e um exemplo de consumo em Python ficam em
[SIMPLICIO_INTEGRATION.md](SIMPLICIO_INTEGRATION.md).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
},
"scripts": {
"test": "node --test",
"test:coverage": "node --test --experimental-test-coverage",
"test:coverage": "node scripts/coverage.js",
"test:cli": "node bin/cli.js --help",
"test:e2e": "playwright test",
"lint": "node scripts/lint.js",
Expand Down
65 changes: 65 additions & 0 deletions scripts/coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node
'use strict';

// Runs the Node.js built-in test runner with V8 coverage and converts the
// "all files" summary row into coverage/coverage-summary.json (Istanbul
// json-summary shape) so the DoD gate can read total.lines.pct.
//
// Parses the textual coverage table instead of the lcov reporter so it works
// across the Node 20 and 22 CI matrix (the lcov reporter is not available on
// every supported version). Dependency-free.

const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');

const COVERAGE_DIR = path.resolve(process.cwd(), 'coverage');
const SUMMARY_PATH = path.join(COVERAGE_DIR, 'coverage-summary.json');

fs.mkdirSync(COVERAGE_DIR, { recursive: true });

const result = spawnSync(
process.execPath,
['--test', '--experimental-test-coverage'],
{ encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },
);

if (result.error) {
console.error(`coverage run failed to start: ${result.error.message}`);
process.exit(1);
}

const stdout = result.stdout || '';
const stderr = result.stderr || '';
process.stdout.write(stdout);
if (stderr) process.stderr.write(stderr);

// Matches the summary row, tolerating the "# " (tap) or "ℹ " (spec) prefix:
// all files | 91.32 | 69.97 | 88.39 |
const match = stdout.match(/all files\s*\|\s*([0-9.]+)\s*\|\s*([0-9.]+)\s*\|\s*([0-9.]+)/);

if (!match) {
if (result.status !== 0) process.exit(result.status);
console.error('::error::could not parse coverage summary row from test output');
process.exit(1);
}

const linesPct = Number(match[1]);
const branchesPct = Number(match[2]);
const functionsPct = Number(match[3]);
const metric = (pct) => ({ total: 0, covered: 0, skipped: 0, pct });

const summary = {
total: {
lines: metric(linesPct),
statements: metric(linesPct),
functions: metric(functionsPct),
branches: metric(branchesPct),
},
};

fs.writeFileSync(SUMMARY_PATH, JSON.stringify(summary, null, 2) + '\n');
console.log(`coverage summary written to ${path.relative(process.cwd(), SUMMARY_PATH)} `
+ `(lines ${linesPct}%, functions ${functionsPct}%, branches ${branchesPct}%)`);

process.exit(result.status === null ? 1 : result.status);
Loading
Loading