azcodr 2.2.0 → 2.3.0
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.
- package/.agents/scripts/agent_guard.js +63 -0
- package/.agents/skills/lets-build/SKILL.md +1 -1
- package/.github/workflows/ci.yml +8 -0
- package/CONTRIBUTING.md +79 -0
- package/README.md +4 -3
- package/SECURITY.md +32 -0
- package/data/memory.template +36 -0
- package/docs/rules/clean_code.md +13 -0
- package/docs/tipping-points.md +37 -0
- package/lib/agent-guard-command.d.ts +28 -0
- package/lib/agent-guard-command.d.ts.map +1 -0
- package/lib/agent-guard-command.js +103 -0
- package/lib/agent-guard-command.js.map +1 -0
- package/lib/agent-guard-file.d.ts +32 -0
- package/lib/agent-guard-file.d.ts.map +1 -0
- package/lib/agent-guard-file.js +77 -0
- package/lib/agent-guard-file.js.map +1 -0
- package/lib/agent-guard-tdd.d.ts +34 -0
- package/lib/agent-guard-tdd.d.ts.map +1 -0
- package/lib/agent-guard-tdd.js +77 -0
- package/lib/agent-guard-tdd.js.map +1 -0
- package/lib/agent-guard.d.ts +38 -0
- package/lib/agent-guard.d.ts.map +1 -0
- package/lib/agent-guard.js +80 -0
- package/lib/agent-guard.js.map +1 -0
- package/lib/boundaries.d.ts +41 -0
- package/lib/boundaries.d.ts.map +1 -0
- package/lib/boundaries.js +158 -0
- package/lib/boundaries.js.map +1 -0
- package/lib/cli-parse.d.ts +4 -0
- package/lib/cli-parse.d.ts.map +1 -1
- package/lib/cli-parse.js +23 -5
- package/lib/cli-parse.js.map +1 -1
- package/lib/cli.d.ts +14 -0
- package/lib/cli.d.ts.map +1 -1
- package/lib/cli.js +33 -5
- package/lib/cli.js.map +1 -1
- package/lib/guards.d.ts +6 -0
- package/lib/guards.d.ts.map +1 -1
- package/lib/guards.js +10 -1
- package/lib/guards.js.map +1 -1
- package/lib/index.d.ts +15 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -1
- package/lib/repo.js +4 -4
- package/lib/repo.js.map +1 -1
- package/lib/scaffold.d.ts +2 -7
- package/lib/scaffold.d.ts.map +1 -1
- package/lib/scaffold.js +7 -10
- package/lib/scaffold.js.map +1 -1
- package/lib/validate.d.ts +33 -0
- package/lib/validate.d.ts.map +1 -0
- package/lib/validate.js +24 -0
- package/lib/validate.js.map +1 -0
- package/memory.md +8 -0
- package/package.json +4 -2
- package/scripts/validate/links.js +0 -1
- package/scripts/validate/root.js +1 -1
- package/src/agent-guard-command.ts +118 -0
- package/src/agent-guard-file.ts +107 -0
- package/src/agent-guard-tdd.ts +96 -0
- package/src/agent-guard.ts +123 -0
- package/src/boundaries.ts +198 -0
- package/src/cli-parse.ts +26 -4
- package/src/cli.ts +41 -15
- package/src/guards.ts +11 -1
- package/src/index.ts +40 -0
- package/src/repo.ts +4 -4
- package/src/scaffold.ts +7 -10
- package/src/validate.ts +47 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-platform PreToolUse safety & architectural enforcement hook.
|
|
5
|
+
*
|
|
6
|
+
* Enforces:
|
|
7
|
+
* 1. Command safety (blocks destructive rm, force-push, drop db, etc.)
|
|
8
|
+
* 2. Refactor-Before-Add (blocks adding lines to files over line budget)
|
|
9
|
+
* 3. Test-First / RED-before-GREEN (when enabled)
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import process from 'node:process';
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const selfDir = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const guardModulePath = path.resolve(selfDir, '../../lib/agent-guard.js');
|
|
18
|
+
|
|
19
|
+
async function getGuardModule() {
|
|
20
|
+
return import(pathToFileURL(guardModulePath).href);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readStdin(timeoutMs = 1500) {
|
|
24
|
+
if (process.stdin.isTTY) return '';
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let acc = '';
|
|
27
|
+
const timer = setTimeout(() => resolve(acc), timeoutMs);
|
|
28
|
+
process.stdin.setEncoding('utf-8');
|
|
29
|
+
process.stdin.on('data', (chunk) => { acc += chunk; });
|
|
30
|
+
process.stdin.on('end', () => {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
resolve(acc);
|
|
33
|
+
});
|
|
34
|
+
process.stdin.on('error', () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
resolve(acc);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
const argvInput = process.argv.slice(2).join(' ').trim();
|
|
43
|
+
const stdinInput = (await readStdin()).trim();
|
|
44
|
+
const rawInput = argvInput || stdinInput;
|
|
45
|
+
|
|
46
|
+
if (!rawInput) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { inspectPreTool } = await getGuardModule();
|
|
51
|
+
const decision = inspectPreTool(rawInput, {
|
|
52
|
+
workspaceRoot: process.cwd()
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!decision.allowed) {
|
|
56
|
+
console.error(`🚨 Architectural Guard: ${decision.reason}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
main().catch(() => process.exit(0));
|
|
@@ -100,7 +100,7 @@ Upon user confirmation:
|
|
|
100
100
|
- *Extension:* `manifest.json`, `src/background/index.ts`, `src/content/index.ts`, `src/popup/index.html`.
|
|
101
101
|
- *Game / Engine:* `src/core/`, `src/ecs/`, asset manifest, frame loop entrypoint.
|
|
102
102
|
- *CLI:* `src/cmd/`, `src/core/`, CLI entrypoint with exit code handling.
|
|
103
|
-
3. Generate build manifests (`Cargo.toml`, `package.json`, `go.mod`, `pyproject.toml`), linter configurations, and boundary smoke test (`scripts/smoke_test.sh`).
|
|
103
|
+
3. Generate build manifests (`Cargo.toml`, `package.json`, `go.mod`, `pyproject.toml`), deterministic linter configurations strictly enforcing the [Polyglot Fitness Function Standards](../../../docs/rules/clean_code.md#polyglot-fitness-function-standards), and boundary smoke test (`scripts/smoke_test.sh`).
|
|
104
104
|
4. **Replace Starter README with Project-Specific README**:
|
|
105
105
|
Generate a clean, project-specific `README.md` using [references/project_readme_template.md](./references/project_readme_template.md), completely replacing meta-template content with the project's actual name, mission, stack highlights, quickstart commands, and directory tree.
|
|
106
106
|
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -84,6 +84,14 @@ jobs:
|
|
|
84
84
|
- name: Verify Coverage Gates
|
|
85
85
|
run: npm run test:coverage
|
|
86
86
|
|
|
87
|
+
- name: Upload Coverage Report
|
|
88
|
+
uses: actions/upload-artifact@v4
|
|
89
|
+
if: always()
|
|
90
|
+
with:
|
|
91
|
+
name: coverage-report
|
|
92
|
+
path: coverage/
|
|
93
|
+
retention-days: 14
|
|
94
|
+
|
|
87
95
|
shell-syntax:
|
|
88
96
|
name: Shell Script Syntax (shipped hooks)
|
|
89
97
|
runs-on: ubuntu-24.04
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Contributing to azcodr
|
|
2
|
+
|
|
3
|
+
Thank you for contributing to `azcodr`!
|
|
4
|
+
|
|
5
|
+
This repository is governed by strict systemic atomicity, Clean Code fitness functions, and test-driven development.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🛠️ Development Setup
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
- Node.js `>=22.8.0` (required for native ESM VM modules)
|
|
13
|
+
- npm `>=10.0.0`
|
|
14
|
+
- Git
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
|
+
Clone the repository and install exact-pinned development tooling:
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/prosubodh/azcodr.git
|
|
20
|
+
cd azcodr
|
|
21
|
+
npm ci
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🧪 Verification & Quality Gates
|
|
27
|
+
|
|
28
|
+
Before submitting any code changes, all 5 quality gates must pass with zero errors:
|
|
29
|
+
|
|
30
|
+
1. **Type Checking:**
|
|
31
|
+
```bash
|
|
32
|
+
npm run typecheck
|
|
33
|
+
```
|
|
34
|
+
2. **Production Build:**
|
|
35
|
+
```bash
|
|
36
|
+
npm run build
|
|
37
|
+
```
|
|
38
|
+
3. **Clean Code Fitness Functions (ESLint):**
|
|
39
|
+
```bash
|
|
40
|
+
npm run lint
|
|
41
|
+
```
|
|
42
|
+
*Enforces:*
|
|
43
|
+
- Maximum 300 lines per file (including comments)
|
|
44
|
+
- Maximum 30 lines per function
|
|
45
|
+
- Cyclomatic complexity $\le 10$
|
|
46
|
+
- Maximum 3 parameters per function
|
|
47
|
+
4. **Test Suite & Coverage Gate (Jest):**
|
|
48
|
+
```bash
|
|
49
|
+
npm test
|
|
50
|
+
npm run test:coverage
|
|
51
|
+
```
|
|
52
|
+
*Coverage gates enforced:*
|
|
53
|
+
- **100% lines coverage**
|
|
54
|
+
- **97% functions coverage**
|
|
55
|
+
- **98% branches coverage**
|
|
56
|
+
5. **Agentic Architecture Validation:**
|
|
57
|
+
```bash
|
|
58
|
+
npm run validate
|
|
59
|
+
```
|
|
60
|
+
Validates root harness parity, progressive disclosure rules, skill formats, markdown links, and ADR ledger consistency.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 🏛️ Architectural Invariants
|
|
65
|
+
|
|
66
|
+
Every contribution must preserve these core invariants:
|
|
67
|
+
- **Zero Runtime Dependencies:** `dependencies` in `package.json` must remain undefined. Runtime features rely strictly on Node.js standard builtins (`node:*`).
|
|
68
|
+
- **Exact-Pinned DevDependencies:** Any tool in `devDependencies` must have an exact version (no `^`, `~`, or floating ranges) and committed `package-lock.json`.
|
|
69
|
+
- **Refactor Before Adding:** When introducing changes, refactor structure first under existing green tests before adding new logic.
|
|
70
|
+
- **Lightweight ADRs:** Significant architectural decisions or boundary changes must be recorded in `memory.md`.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🔄 Pull Request Guidelines
|
|
75
|
+
|
|
76
|
+
1. Create a feature branch: `git checkout -b feat/your-feature-name`.
|
|
77
|
+
2. Follow Conventional Commits: `feat(...)`, `fix(...)`, `docs(...)`, `test(...)`.
|
|
78
|
+
3. Ensure all tests and validation pass locally (`npm run prepublishOnly`).
|
|
79
|
+
4. Submit your pull request with a concise summary and verification evidence.
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# azcodr: Enterprise Architecture & Agentic Engineering Starter Template
|
|
2
2
|
|
|
3
|
-
> **Production-
|
|
3
|
+
> **Production-oriented architecture governance and topology-scoped scaffolding for AI-assisted development (early release).**
|
|
4
|
+
> *AI agents move fast. Architecture drifts. Azcodr turns architectural intent into executable constraints your agent cannot skip.*
|
|
4
5
|
|
|
5
6
|
---
|
|
6
7
|
|
|
@@ -8,7 +9,7 @@
|
|
|
8
9
|
|
|
9
10
|
1. **Problem-First & Topology Alignment**: Architecture emerges strictly from problem constraints and execution targets (Problem-First; zero preemptive tool bias). Architectural styles match the problem topology: Hexagonal for enterprise backends, Platform Scripting for extensions, Data-Oriented Design for game engines, Command Pipeline for CLIs, and Game Loop for canvas games.
|
|
10
11
|
2. **Systemic Atomicity**: Every rule, skill, database transaction, and code unit adheres to the Single Responsibility Principle (SRP)—indivisible, self-contained, orthogonal, and composable with zero conjunction naming.
|
|
11
|
-
3. **Evolutionary Architecture & Refactor-Before-Add**: To
|
|
12
|
+
3. **Evolutionary Architecture & Refactor-Before-Add**: To catch AI-accelerated architectural drift, code graduates across [5 deterministic tipping points](./docs/tipping-points.md). Refactor structure first under existing green tests before implementing new features. Never append code into rotting files.
|
|
12
13
|
4. **True Incremental TDD & Nano-Cycles**: Prohibit batch-test dumps ("Test-First Waterfall"). Follow Uncle Bob's Three Laws: write one micro-assertion at a time, verify RED failure output, write minimal code to turn GREEN, and refactor under green with Ping-Pong pair programming.
|
|
13
14
|
5. **100% Open-Source & Open Standards**: Standardized exclusively on open-source solutions and vendor-neutral specifications (OpenTelemetry, OPA, OpenFGA, Protocol Buffers, OpenAPI 3.1, JSON Schema Draft 2020-12, CloudEvents, Semgrep, Trivy, Gitleaks, Cosign).
|
|
14
15
|
6. **Zero-Assumption Framework**: Ground truth is established solely through workspace configurations, code evidence, or direct user confirmation.
|
|
@@ -165,4 +166,4 @@ The scaffolder declares two expected capabilities:
|
|
|
165
166
|
|
|
166
167
|
Failures carry stable machine-readable codes (`E_TARGET_NOT_EMPTY`, `E_GIT_BLOCKED`, `E_PATH_ESCAPE`, ...). Branch on `err.code`, never on message text — see the exported `ERROR_CODES` map and `ScaffoldError` type.
|
|
167
168
|
|
|
168
|
-
Initial-commit fallback identity is `
|
|
169
|
+
Initial-commit fallback identity is `azcodr[bot] <bot@azcodr.internal>` via `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env (no `git -c` shell flags).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 2.x | :white_check_mark: |
|
|
8
|
+
| 1.x | :x: |
|
|
9
|
+
|
|
10
|
+
## Reporting a Vulnerability
|
|
11
|
+
|
|
12
|
+
We take the security of `azcodr` and the architectures scaffolded with it seriously. If you discover a security vulnerability, please report it responsibly:
|
|
13
|
+
|
|
14
|
+
1. **GitHub Private Vulnerability Reporting (Preferred):**
|
|
15
|
+
Submit a private advisory directly through [GitHub Security Advisories](https://github.com/prosubodh/azcodr/security/advisories/new).
|
|
16
|
+
2. **Email Disclosure:**
|
|
17
|
+
Email `prosubodh+security@gmail.com` with:
|
|
18
|
+
- Detailed description of the vulnerability and potential impact.
|
|
19
|
+
- Minimal, reproducible steps or proof-of-concept payload.
|
|
20
|
+
- Affected subsystem (`src/guards.ts`, `src/scaffold.ts`, `.agents/scripts/`, etc.).
|
|
21
|
+
|
|
22
|
+
### Response Commitments
|
|
23
|
+
- **Initial Acknowledgment:** Within 48 hours.
|
|
24
|
+
- **Triage & Reproduction:** Within 5 business days.
|
|
25
|
+
- **Coordinated Disclosure:** Security patch release with accompanying advisory disclosure once remediated.
|
|
26
|
+
|
|
27
|
+
### Scope & Hardening Invariants
|
|
28
|
+
The core supply-chain invariants defended by `azcodr` include:
|
|
29
|
+
- **Zero Runtime Dependencies:** Supply chain attack surface bounded to standard library Node.js builtins.
|
|
30
|
+
- **Strict Process Isolation:** No shell-string execution (`execSync`/`exec` forbidden; `execFileSync` requires `shell: false`).
|
|
31
|
+
- **Filesystem Containment:** Path boundary enforcement (`assertInside`) preventing directory traversal or escape.
|
|
32
|
+
- **Target Protection:** Refusal to write into system root, user home directory, or ancestors (`isProtectedTarget`), even with `--force`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Workspace Memory, Architecture Decisions & Knowledge Hub
|
|
2
|
+
|
|
3
|
+
> **Core Purpose:** Authoritative persistent memory ledger for the workspace repository (`./`), maintaining Lightweight Architectural Decision Records (ADRs), system topologies, and living domain contracts.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Quick Navigation & Knowledge Repositories
|
|
8
|
+
|
|
9
|
+
- 📖 **[Living Ubiquitous Language Glossary](./docs/knowledge/ubiquitous_language.md)**: Authoritative, single-name domain vocabulary contract.
|
|
10
|
+
- 📜 **[Lightweight ADR Master Index](#adr-master-index)**: Summary of all architectural decisions and direct links to governing rules.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 2. Consolidated Architectural Decision Records (ADRs)
|
|
15
|
+
|
|
16
|
+
### ADR Master Index
|
|
17
|
+
|
|
18
|
+
| ID | Title | Date | Status | Governing Rule / Skill |
|
|
19
|
+
|---|---|---|---|---|
|
|
20
|
+
| *(No decisions recorded yet)* | *Record initial architecture decisions during Phase 3 of /lets-build.* | *YYYY-MM-DD* | *ACCEPTED* | *e.g. [`clean_code.md`](./docs/rules/clean_code.md)* |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
### Lightweight Decision Summaries
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
Record project Architectural Decision Records (ADRs) below as decisions are finalized.
|
|
28
|
+
Format:
|
|
29
|
+
|
|
30
|
+
#### ADR-001: [Imperative Title]
|
|
31
|
+
- **Date:** YYYY-MM-DD | **Status:** ACCEPTED
|
|
32
|
+
- **Context:** Problem space, constraints, and operational context requiring a decision.
|
|
33
|
+
- **Decision:** Chosen architecture, invariants, and implementation patterns.
|
|
34
|
+
- **Consequences:** Positive benefits and deliberate trade-offs accepted.
|
|
35
|
+
- **Enforced In:** Relevant rule files in docs/rules/ or code paths.
|
|
36
|
+
-->
|
package/docs/rules/clean_code.md
CHANGED
|
@@ -60,3 +60,16 @@ Prevent AI-generated code rot using automated fitness functions integrated into
|
|
|
60
60
|
- **Dependency Direction Gates**: Enforce unidirectional import rules (e.g. `import/no-restricted-paths`, `dependency-cruiser`, `ArchUnit`) ensuring domain core never imports infrastructure or transport adapters.
|
|
61
61
|
- **Complexity Budgets**: Enforce cyclomatic complexity limits (maximum 10 per function).
|
|
62
62
|
If an AI attempt to add code violates any fitness function, the build fails immediately, blocking completion until the architecture is refactored.
|
|
63
|
+
|
|
64
|
+
### Polyglot Fitness Function Standards
|
|
65
|
+
|
|
66
|
+
When bootstrapping non-TypeScript projects via `/lets-build`, the agent must instantiate deterministic, automated fitness function gates matching these exact rules:
|
|
67
|
+
|
|
68
|
+
| Language | Linter / Static Tool | Max File Lines (300) | Max Function Lines (30) | Max Complexity (10) | Max Arguments (3) |
|
|
69
|
+
|---|---|---|---|---|---|
|
|
70
|
+
| **TypeScript / Node** | `eslint` | `max-lines: [error, 300]` | `max-lines-per-function: [error, 30]` | `complexity: [error, 10]` | `max-params: [error, 3]` |
|
|
71
|
+
| **Python** | `ruff` + `flake8` | `flake8-lines: max-line-count = 300` | `flake8-functions: max-function-length = 30` | `mccabe: max-complexity = 10` | `flake8-functions: max-parameters = 3` |
|
|
72
|
+
| **Rust** | `cargo clippy` | `#![deny(clippy::too_many_lines)]` | `clippy::too_many_lines = 30` | `clippy::cognitive_complexity = 10` | `clippy::too_many_arguments = 3` |
|
|
73
|
+
| **Go** | `golangci-lint` | `maintidx: 300` | `funlen: lines = 30` | `gocyclo: min-complexity = 10` | `funlen: statements = 25` |
|
|
74
|
+
| **Java** | `Checkstyle` + `ArchUnit` | `FileLength: max 300` | `MethodLength: max 30` | `CyclomaticComplexity: max 10` | `ParameterNumber: max 3` |
|
|
75
|
+
| **C# / .NET** | `.editorconfig` + Roslyn | `file_length = 300:error` | `method_length = 30:error` | `CA1502: Avoid excessive complexity <= 10` | `CA1501: max 3 params` |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Deterministic Architectural Tipping Points
|
|
2
|
+
|
|
3
|
+
> **Core Mandate:** Architecture evolves incrementally as complexity grows. AI coding agents naturally take the path of least resistance (local token minimization), repeatedly appending code to simple files until they rot into a Big Ball of Mud. Whenever code crosses one of the 5 Deterministic Tipping Points, the agent must pause and execute an architectural refactor under green tests before adding new features.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The 5 Deterministic Tipping Points
|
|
8
|
+
|
|
9
|
+
| # | Simple Baseline (Day 1) | Tipping Point / Mutation Trigger | Required Architectural Upgrade |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| **1** | **Flat Script / Single File** | File exceeds **250 lines**, coordinates **>2 distinct I/O resources**, or is imported by **>3 distinct callers**. | **Extract Modular Subsystems:** Decouple domain logic from platform I/O; split into dedicated, cohesive submodules. |
|
|
12
|
+
| **2** | **Inline Branching (`if/else` / `switch`)** | **Rule of Three:** The 3rd branching variant, payment provider, or protocol format is introduced. | **Strategy Pattern / Registry:** Replace conditional cascades with a polymorphic Strategy interface or handler registry; update `memory.md`. |
|
|
13
|
+
| **3** | **In-Memory Store / Global State** | State requires **concurrent mutations**, **persistence across restarts**, or **transactional rollback**. | **Repository Pattern & Persistence Port:** Introduce an explicit storage port contract; swap in-memory mock for a persistent database adapter. |
|
|
14
|
+
| **4** | **Direct Platform / Third-Party SDK Calls** | External SDK or platform API is called from **>2 places**, or throws untyped exceptions across boundaries. | **Adapter Pattern (Anti-Corruption Layer):** Wrap external SDK inside an application-owned port interface; mock only the owned interface in tests. |
|
|
15
|
+
| **5** | **Monolithic Domain Model** | The same business noun represents divergent lifecycles or definitions across workflows (e.g. `User` in Auth vs `User` in Billing). | **Bounded Context Split:** Separate into isolated domain contexts with explicit DTO / Anti-Corruption translation between them. |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Refactor-Before-Add Protocol (Kent Beck's Rule)
|
|
20
|
+
|
|
21
|
+
> *"Make the change easy (warning: this may be hard), then make the easy change."* — Kent Beck
|
|
22
|
+
|
|
23
|
+
Before writing production code for any new feature or user story:
|
|
24
|
+
|
|
25
|
+
1. **Assess Tipping Points**: Will adding this requirement cause any module, function, or data structure to cross an architectural tipping point?
|
|
26
|
+
2. **Phase A — Structural Refactoring (Under Green)**: If yes, refactor the existing architecture *first* while existing test suites remain 100% green. Zero behavioral changes; purely structural evolution.
|
|
27
|
+
3. **Phase B — ADR Mutation**: When an architectural tipping point is crossed, log a Lightweight Architectural Decision Record in `memory.md` summarizing the new structural boundary and trade-off.
|
|
28
|
+
4. **Phase C — Feature Implementation (Inner TDD)**: Only once the architecture cleanly accommodates the new capability, write the failing micro-test and implement the feature.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## References
|
|
33
|
+
|
|
34
|
+
- [Clean Code & Pragmatic Directives](./rules/clean_code.md)
|
|
35
|
+
- [Design Patterns & Result Types](./rules/design_patterns.md)
|
|
36
|
+
- [Domain-Driven Design](./rules/domain_driven_design.md)
|
|
37
|
+
- [Test-Driven Development](./rules/test_driven_development.md)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command safety guard inspecting shell commands against a destructive denylist.
|
|
3
|
+
*/
|
|
4
|
+
export interface CommandViolation {
|
|
5
|
+
blocked: boolean;
|
|
6
|
+
reason?: string;
|
|
7
|
+
command?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DenylistRule {
|
|
10
|
+
pattern: RegExp;
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const DESTRUCTIVE_RULES: readonly DenylistRule[];
|
|
14
|
+
export declare function normalizeCommand(raw: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Checks a command string against the safety denylist.
|
|
17
|
+
*
|
|
18
|
+
* @param command - Raw shell command string.
|
|
19
|
+
* @returns Violation details if blocked, or clean outcome.
|
|
20
|
+
*/
|
|
21
|
+
export declare function inspectCommand(command: string): CommandViolation;
|
|
22
|
+
declare const _default: {
|
|
23
|
+
DESTRUCTIVE_RULES: readonly DenylistRule[];
|
|
24
|
+
normalizeCommand: typeof normalizeCommand;
|
|
25
|
+
inspectCommand: typeof inspectCommand;
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|
|
28
|
+
//# sourceMappingURL=agent-guard-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-command.d.ts","sourceRoot":"","sources":["../src/agent-guard-command.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,YAAY,EAyEpD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAehE;;;;;;AAED,wBAAuE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command safety guard inspecting shell commands against a destructive denylist.
|
|
3
|
+
*/
|
|
4
|
+
export const DESTRUCTIVE_RULES = [
|
|
5
|
+
{
|
|
6
|
+
pattern: /\brm\s+(-[a-z-]*[rf][a-z-]*\s+)+(\/|\/\*|~|~\/\*|\$home|\$\{home\}|\.|\.\.|\.\/|[a-z]:\\|[a-z]:\/)/i,
|
|
7
|
+
reason: 'destructive rm targeting root, home, drive, or the current directory'
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
pattern: /(^|\s)rm\s+(-[a-z-]*\s+)*(\/|~|[a-z]:\\|[a-z]:\/)($|\s)/i,
|
|
11
|
+
reason: 'destructive rm targeting root or home'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
pattern: /\brm\s+--recursive.*--force\s+(\/|~|\$home|\.)/i,
|
|
15
|
+
reason: 'destructive rm using long-form flags'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
pattern: /\brm\s+.*--no-preserve-root/i,
|
|
19
|
+
reason: 'rm --no-preserve-root is forbidden under agent execution'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /\b(wipefs\s+-a|shred\s+-u|find\s+.*-delete)\b/i,
|
|
23
|
+
reason: 'irreversible disk or mass-delete operation'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
pattern: /\bgit\s+push\s+.*(-f|--force|--mirror)\b/i,
|
|
27
|
+
reason: 'force-push or mirror-push can destroy remote history'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pattern: /\bgit\s+push\b.*--force-with-lease\b/i,
|
|
31
|
+
reason: 'force-push variant detected'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /\bgit\s+(reset\s+--hard|clean\s+-[a-z]*[fd])/i,
|
|
35
|
+
reason: 'destructive git worktree operation (reset --hard / clean)'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: /\bgit\s+checkout\s+--\s+\./i,
|
|
39
|
+
reason: 'git checkout -- . discards all uncommitted work'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
pattern: /\bgit\s+branch\s+-D\b/i,
|
|
43
|
+
reason: 'force branch delete'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
pattern: /\b(drop\s+(database|schema|table)|truncate\s+(table\s+)?[a-z_])/i,
|
|
47
|
+
reason: 'destructive SQL DDL'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
pattern: /\b(delete\s+from|update\s+[a-z_]+\s+set|alter\s+table)\b/i,
|
|
51
|
+
reason: 'destructive SQL DML'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /\b(grant\s+all|revoke\s+all)\b/i,
|
|
55
|
+
reason: 'privilege escalation in SQL'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
pattern: /\b(mkfs|dd\s+if=|fdisk|parted\s+\/dev)\b/i,
|
|
59
|
+
reason: 'raw disk operation'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
pattern: /(:[\s]*\([\s]*\)[\s]*\{|:[\s]*\|[\s]*:)/,
|
|
63
|
+
reason: 'fork bomb'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
pattern: /\b(curl|wget)\s+.*\|\s*(sudo\s+)?(ba)?sh\b/i,
|
|
67
|
+
reason: 'piping download directly into shell execution'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
pattern: /\b(base64\s+-d|base64\s+--decode)\s+.*\|/i,
|
|
71
|
+
reason: 'decoding encoded payload into a pipe'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
pattern: /\b(npm|yarn|pnpm)\s+publish\b/i,
|
|
75
|
+
reason: 'package publish must run via release workflow, not ad-hoc tool call'
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
export function normalizeCommand(raw) {
|
|
79
|
+
return raw.replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks a command string against the safety denylist.
|
|
83
|
+
*
|
|
84
|
+
* @param command - Raw shell command string.
|
|
85
|
+
* @returns Violation details if blocked, or clean outcome.
|
|
86
|
+
*/
|
|
87
|
+
export function inspectCommand(command) {
|
|
88
|
+
const normalized = normalizeCommand(command);
|
|
89
|
+
if (!normalized)
|
|
90
|
+
return { blocked: false };
|
|
91
|
+
for (const rule of DESTRUCTIVE_RULES) {
|
|
92
|
+
if (rule.pattern.test(normalized)) {
|
|
93
|
+
return {
|
|
94
|
+
blocked: true,
|
|
95
|
+
reason: rule.reason,
|
|
96
|
+
command: normalized.slice(0, 300)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { blocked: false, command: normalized };
|
|
101
|
+
}
|
|
102
|
+
export default { DESTRUCTIVE_RULES, normalizeCommand, inspectCommand };
|
|
103
|
+
//# sourceMappingURL=agent-guard-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-command.js","sourceRoot":"","sources":["../src/agent-guard-command.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,CAAC,MAAM,iBAAiB,GAA4B;IACxD;QACE,OAAO,EAAE,qGAAqG;QAC9G,MAAM,EAAE,sEAAsE;KAC/E;IACD;QACE,OAAO,EAAE,0DAA0D;QACnE,MAAM,EAAE,uCAAuC;KAChD;IACD;QACE,OAAO,EAAE,iDAAiD;QAC1D,MAAM,EAAE,sCAAsC;KAC/C;IACD;QACE,OAAO,EAAE,8BAA8B;QACvC,MAAM,EAAE,0DAA0D;KACnE;IACD;QACE,OAAO,EAAE,gDAAgD;QACzD,MAAM,EAAE,4CAA4C;KACrD;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,sDAAsD;KAC/D;IACD;QACE,OAAO,EAAE,uCAAuC;QAChD,MAAM,EAAE,6BAA6B;KACtC;IACD;QACE,OAAO,EAAE,+CAA+C;QACxD,MAAM,EAAE,2DAA2D;KACpE;IACD;QACE,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,iDAAiD;KAC1D;IACD;QACE,OAAO,EAAE,wBAAwB;QACjC,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,kEAAkE;QAC3E,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,2DAA2D;QACpE,MAAM,EAAE,qBAAqB;KAC9B;IACD;QACE,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EAAE,6BAA6B;KACtC;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,oBAAoB;KAC7B;IACD;QACE,OAAO,EAAE,yCAAyC;QAClD,MAAM,EAAE,WAAW;KACpB;IACD;QACE,OAAO,EAAE,6CAA6C;QACtD,MAAM,EAAE,+CAA+C;KACxD;IACD;QACE,OAAO,EAAE,2CAA2C;QACpD,MAAM,EAAE,sCAAsC;KAC/C;IACD;QACE,OAAO,EAAE,gCAAgC;QACzC,MAAM,EAAE,qEAAqE;KAC9E;CACF,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAE3C,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAClC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACjD,CAAC;AAED,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const DEFAULT_MAX_FILE_LINES = 300;
|
|
2
|
+
export interface FileCheckInput {
|
|
3
|
+
filePath: string;
|
|
4
|
+
incomingContent?: string;
|
|
5
|
+
replacementContent?: string;
|
|
6
|
+
targetContent?: string;
|
|
7
|
+
maxLines?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FileCheckResult {
|
|
10
|
+
blocked: boolean;
|
|
11
|
+
reason?: string;
|
|
12
|
+
currentLines: number;
|
|
13
|
+
projectedLines: number;
|
|
14
|
+
maxLines: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function countLines(text: string): number;
|
|
17
|
+
export declare function readExistingLines(filePath: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Checks a proposed file write or edit against Refactor-Before-Add rules.
|
|
20
|
+
*
|
|
21
|
+
* @param input - File path and proposed content.
|
|
22
|
+
* @returns Decision result indicating whether the write is blocked.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inspectFileWrite(input: FileCheckInput): FileCheckResult;
|
|
25
|
+
declare const _default: {
|
|
26
|
+
DEFAULT_MAX_FILE_LINES: number;
|
|
27
|
+
countLines: typeof countLines;
|
|
28
|
+
readExistingLines: typeof readExistingLines;
|
|
29
|
+
inspectFileWrite: typeof inspectFileWrite;
|
|
30
|
+
};
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=agent-guard-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-file.d.ts","sourceRoot":"","sources":["../src/agent-guard-file.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAgDD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAWvE;;;;;;;AAED,wBAKE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const DEFAULT_MAX_FILE_LINES = 300;
|
|
4
|
+
export function countLines(text) {
|
|
5
|
+
if (!text)
|
|
6
|
+
return 0;
|
|
7
|
+
return text.split('\n').length;
|
|
8
|
+
}
|
|
9
|
+
export function readExistingLines(filePath) {
|
|
10
|
+
if (!fs.existsSync(filePath))
|
|
11
|
+
return 0;
|
|
12
|
+
try {
|
|
13
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
return countLines(content);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function calculateProjectedLines(currentLines, input) {
|
|
21
|
+
if (input.incomingContent !== undefined) {
|
|
22
|
+
return countLines(input.incomingContent);
|
|
23
|
+
}
|
|
24
|
+
if (input.replacementContent !== undefined) {
|
|
25
|
+
const addedLines = countLines(input.replacementContent);
|
|
26
|
+
const removedLines = input.targetContent !== undefined ? countLines(input.targetContent) : 1;
|
|
27
|
+
return Math.max(0, currentLines + addedLines - removedLines);
|
|
28
|
+
}
|
|
29
|
+
return currentLines;
|
|
30
|
+
}
|
|
31
|
+
function evaluateLineThresholds(params) {
|
|
32
|
+
const { filePath, currentLines, projectedLines, maxLines } = params;
|
|
33
|
+
const base = { currentLines, projectedLines, maxLines };
|
|
34
|
+
const relPath = path.basename(filePath);
|
|
35
|
+
if (currentLines > maxLines) {
|
|
36
|
+
if (projectedLines >= currentLines) {
|
|
37
|
+
return {
|
|
38
|
+
...base,
|
|
39
|
+
blocked: true,
|
|
40
|
+
reason: `Refactor-Before-Add violation: '${relPath}' currently has ${currentLines} lines (limit: ${maxLines}). Adding or maintaining lines in an overflowing file is blocked. Refactor into modular units under green tests first.`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { ...base, blocked: false };
|
|
44
|
+
}
|
|
45
|
+
if (projectedLines > maxLines) {
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
blocked: true,
|
|
49
|
+
reason: `File line budget exceeded: '${relPath}' would grow to ${projectedLines} lines (limit: ${maxLines}). Decompose into modular components before appending more code.`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { ...base, blocked: false };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Checks a proposed file write or edit against Refactor-Before-Add rules.
|
|
56
|
+
*
|
|
57
|
+
* @param input - File path and proposed content.
|
|
58
|
+
* @returns Decision result indicating whether the write is blocked.
|
|
59
|
+
*/
|
|
60
|
+
export function inspectFileWrite(input) {
|
|
61
|
+
const maxLines = input.maxLines ?? DEFAULT_MAX_FILE_LINES;
|
|
62
|
+
const currentLines = readExistingLines(input.filePath);
|
|
63
|
+
const projectedLines = calculateProjectedLines(currentLines, input);
|
|
64
|
+
return evaluateLineThresholds({
|
|
65
|
+
filePath: input.filePath,
|
|
66
|
+
currentLines,
|
|
67
|
+
projectedLines,
|
|
68
|
+
maxLines
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export default {
|
|
72
|
+
DEFAULT_MAX_FILE_LINES,
|
|
73
|
+
countLines,
|
|
74
|
+
readExistingLines,
|
|
75
|
+
inspectFileWrite
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=agent-guard-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-file.js","sourceRoot":"","sources":["../src/agent-guard-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface SessionTddState {
|
|
2
|
+
lastFailingTestRecorded: boolean;
|
|
3
|
+
lastTestRunTime?: number;
|
|
4
|
+
lastTestPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TddCheckResult {
|
|
7
|
+
blocked: boolean;
|
|
8
|
+
reason?: string;
|
|
9
|
+
isProductionFile: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function isTestFile(filePath: string): boolean;
|
|
12
|
+
export declare function isNonCodeFile(filePath: string): boolean;
|
|
13
|
+
export declare function isProductionFile(filePath: string): boolean;
|
|
14
|
+
export declare function readTddState(stateFile: string): SessionTddState;
|
|
15
|
+
export declare function writeTddState(stateFile: string, state: SessionTddState): void;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether an edit to a file is allowed under RED-before-GREEN TDD rules.
|
|
18
|
+
*
|
|
19
|
+
* @param filePath - Path to file being edited.
|
|
20
|
+
* @param state - Current session TDD state.
|
|
21
|
+
* @param enforceTestFirst - Whether test-first enforcement is strictly enabled.
|
|
22
|
+
* @returns Decision result indicating whether edit is blocked.
|
|
23
|
+
*/
|
|
24
|
+
export declare function inspectTddRequirement(filePath: string, state: SessionTddState, enforceTestFirst?: boolean): TddCheckResult;
|
|
25
|
+
declare const _default: {
|
|
26
|
+
isTestFile: typeof isTestFile;
|
|
27
|
+
isNonCodeFile: typeof isNonCodeFile;
|
|
28
|
+
isProductionFile: typeof isProductionFile;
|
|
29
|
+
readTddState: typeof readTddState;
|
|
30
|
+
writeTddState: typeof writeTddState;
|
|
31
|
+
inspectTddRequirement: typeof inspectTddRequirement;
|
|
32
|
+
};
|
|
33
|
+
export default _default;
|
|
34
|
+
//# sourceMappingURL=agent-guard-tdd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-guard-tdd.d.ts","sourceRoot":"","sources":["../src/agent-guard-tdd.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAU/D;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAQ7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,eAAe,EACtB,gBAAgB,GAAE,OAAe,GAChC,cAAc,CAgBhB;;;;;;;;;AAED,wBAOE"}
|