@ghostbyte/agent-docs-kit 4.0.2 → 5.0.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/CHANGELOG.md +32 -0
- package/README.md +43 -37
- package/SECURITY.md +11 -0
- package/content/catalog.json +191 -22
- package/content/documentation/01-system-documentation.md +13 -0
- package/content/documentation/02-project-governance.md +13 -0
- package/content/documentation/03-development-records.md +13 -0
- package/content/documentation/04-testing-evidence.md +13 -0
- package/content/documentation/05-operations-runbooks.md +13 -0
- package/content/documentation/06-future-proposals.md +13 -0
- package/content/documentation/07-archive-governance.md +13 -0
- package/content/documentation/full-reference.md +24 -0
- package/content/engineering/01-query-performance.md +17 -27
- package/content/engineering/02-networking-fundamentals.md +17 -27
- package/content/engineering/03-rate-limiting-algorithms.md +18 -28
- package/content/engineering/04-production-debugging.md +17 -27
- package/content/engineering/05-code-review-practices.md +17 -27
- package/content/engineering/06-data-modeling.md +17 -27
- package/content/engineering/07-cloud-cost-saving.md +17 -27
- package/content/engineering/08-testing-types.md +17 -27
- package/content/engineering/09-message-queues.md +17 -27
- package/content/engineering/10-api-design-compatibility.md +14 -0
- package/content/engineering/11-reliability-slos.md +14 -0
- package/content/engineering/12-observability-telemetry.md +14 -0
- package/content/engineering/13-deployment-change-safety.md +14 -0
- package/content/engineering/14-capacity-concurrency.md +14 -0
- package/content/engineering/full-reference.md +24 -144
- package/content/security/01-design-architecture.md +11 -19
- package/content/security/02-local-development.md +13 -21
- package/content/security/03-database-backend.md +18 -26
- package/content/security/04-api-layer.md +23 -31
- package/content/security/05-frontend-ui.md +16 -24
- package/content/security/06-application-security-testing.md +22 -28
- package/content/security/07-cicd-release-engineering.md +13 -21
- package/content/security/08-cloud-infrastructure.md +17 -25
- package/content/security/09-edge-cdn-dns.md +16 -24
- package/content/security/10-release-rollout.md +13 -21
- package/content/security/11-post-release-monitoring-ir.md +15 -23
- package/content/security/12-ongoing-operations.md +24 -32
- package/content/security/full-reference.md +30 -3163
- package/docs/ARCHITECTURE.md +31 -92
- package/docs/CONTENT_MAINTENANCE.md +5 -1
- package/docs/GITHUB_AND_NPM_SETUP.md +1 -1
- package/docs/RELEASE_PROCESS.md +5 -5
- package/package.json +12 -5
- package/scripts/build-site.mjs +13 -0
- package/scripts/build-skill.mjs +24 -0
- package/scripts/check-links.mjs +56 -0
- package/scripts/check-review-dates.mjs +21 -0
- package/scripts/preview-site.mjs +43 -0
- package/scripts/smoke-package.mjs +58 -0
- package/scripts/validate-content.mjs +85 -0
- package/site/app.js +121 -0
- package/site/index.html +346 -0
- package/site/styles.css +159 -0
- package/skills/manage-project-quality/SKILL.md +18 -2
- package/skills/manage-project-quality/assets/documentation-report.md +3 -3
- package/skills/manage-project-quality/assets/engineering-report.md +3 -3
- package/skills/manage-project-quality/assets/security-report.md +3 -3
- package/skills/manage-project-quality/references/documentation-01-system-documentation.md +13 -0
- package/skills/manage-project-quality/references/documentation-02-project-governance.md +13 -0
- package/skills/manage-project-quality/references/documentation-03-development-records.md +13 -0
- package/skills/manage-project-quality/references/documentation-04-testing-evidence.md +13 -0
- package/skills/manage-project-quality/references/documentation-05-operations-runbooks.md +13 -0
- package/skills/manage-project-quality/references/documentation-06-future-proposals.md +13 -0
- package/skills/manage-project-quality/references/documentation-07-archive-governance.md +13 -0
- package/skills/manage-project-quality/references/documentation-full-reference.md +24 -0
- package/skills/manage-project-quality/references/documentation-governance.md +2 -2
- package/skills/manage-project-quality/references/engineering-01-query-performance.md +17 -27
- package/skills/manage-project-quality/references/engineering-02-networking-fundamentals.md +17 -27
- package/skills/manage-project-quality/references/engineering-03-rate-limiting-algorithms.md +18 -28
- package/skills/manage-project-quality/references/engineering-04-production-debugging.md +17 -27
- package/skills/manage-project-quality/references/engineering-05-code-review-practices.md +17 -27
- package/skills/manage-project-quality/references/engineering-06-data-modeling.md +17 -27
- package/skills/manage-project-quality/references/engineering-07-cloud-cost-saving.md +17 -27
- package/skills/manage-project-quality/references/engineering-08-testing-types.md +17 -27
- package/skills/manage-project-quality/references/engineering-09-message-queues.md +17 -27
- package/skills/manage-project-quality/references/engineering-10-api-design-compatibility.md +14 -0
- package/skills/manage-project-quality/references/engineering-11-reliability-slos.md +14 -0
- package/skills/manage-project-quality/references/engineering-12-observability-telemetry.md +14 -0
- package/skills/manage-project-quality/references/engineering-13-deployment-change-safety.md +14 -0
- package/skills/manage-project-quality/references/engineering-14-capacity-concurrency.md +14 -0
- package/skills/manage-project-quality/references/engineering-full-reference.md +24 -144
- package/skills/manage-project-quality/references/engineering-protocol.md +1 -1
- package/skills/manage-project-quality/references/security-01-design-architecture.md +11 -19
- package/skills/manage-project-quality/references/security-02-local-development.md +13 -21
- package/skills/manage-project-quality/references/security-03-database-backend.md +18 -26
- package/skills/manage-project-quality/references/security-04-api-layer.md +23 -31
- package/skills/manage-project-quality/references/security-05-frontend-ui.md +16 -24
- package/skills/manage-project-quality/references/security-06-application-security-testing.md +22 -28
- package/skills/manage-project-quality/references/security-07-cicd-release-engineering.md +13 -21
- package/skills/manage-project-quality/references/security-08-cloud-infrastructure.md +17 -25
- package/skills/manage-project-quality/references/security-09-edge-cdn-dns.md +16 -24
- package/skills/manage-project-quality/references/security-10-release-rollout.md +13 -21
- package/skills/manage-project-quality/references/security-11-post-release-monitoring-ir.md +15 -23
- package/skills/manage-project-quality/references/security-12-ongoing-operations.md +24 -32
- package/skills/manage-project-quality/references/security-full-reference.md +30 -3163
- package/skills/manage-project-quality/scripts/check-report.mjs +67 -16
- package/src/api/index.mjs +4 -0
- package/src/cli/arguments.mjs +27 -3
- package/src/cli/help.mjs +53 -18
- package/src/cli/main.mjs +66 -9
- package/src/commands/audit.mjs +146 -31
- package/src/commands/doctor.mjs +1 -1
- package/src/commands/guide.mjs +12 -0
- package/src/commands/list.mjs +1 -1
- package/src/commands/setup.mjs +9 -4
- package/src/commands/skill.mjs +1 -0
- package/src/commands/template.mjs +32 -0
- package/src/commands/validate.mjs +36 -4
- package/src/commands/website.mjs +26 -0
- package/src/core/config.mjs +61 -0
- package/src/core/files.mjs +47 -6
- package/templates/adr.md +20 -0
- package/templates/change-record.md +18 -0
- package/templates/incident-review.md +20 -0
- package/templates/runbook.md +18 -0
- package/templates/test-evidence.md +18 -0
- package/tests/cli.test.mjs +226 -0
- package/tests/content.test.mjs +18 -0
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,107 +1,46 @@
|
|
|
1
1
|
# Architecture and file structure
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The package has four layers: CLI/API behavior in `src/`, canonical review content in `content/`, the generated progressive skill in `skills/`, and the static documentation site in `site/`.
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
Agent_Docs_Kit/
|
|
7
|
+
├─ .agent-docs/config.json # this repository's audit policy
|
|
7
8
|
├─ .github/
|
|
9
|
+
│ ├─ dependabot.yml # monthly npm/action update proposals
|
|
8
10
|
│ └─ workflows/
|
|
9
|
-
│ ├─ ci.yml #
|
|
10
|
-
│ ├─
|
|
11
|
-
│
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
│ ├─ ci.yml # Node 22/24/26 cross-platform release gate
|
|
12
|
+
│ ├─ monthly-health.yml # scheduled health and review-date check
|
|
13
|
+
│ ├─ pages.yml # GitHub Pages deployment
|
|
14
|
+
│ └─ publish-npm.yml # npm trusted publishing
|
|
15
|
+
├─ bin/agent-docs.mjs # executable entry point
|
|
14
16
|
├─ src/
|
|
15
|
-
│ ├─ api/
|
|
16
|
-
│
|
|
17
|
-
│ ├─
|
|
18
|
-
│
|
|
19
|
-
│ │ ├─ help.mjs # command reference
|
|
20
|
-
│ │ └─ main.mjs # direct-command and menu router
|
|
21
|
-
│ ├─ commands/
|
|
22
|
-
│ │ ├─ audit.mjs # docs/security report creation
|
|
23
|
-
│ │ ├─ doctor.mjs # prerequisite checks
|
|
24
|
-
│ │ ├─ list.mjs # phase/topic discovery
|
|
25
|
-
│ │ ├─ setup.mjs # project docs scaffold
|
|
26
|
-
│ │ ├─ skill.mjs # explicit skill installation
|
|
27
|
-
│ │ └─ validate.mjs # package integrity checks
|
|
28
|
-
│ └─ core/
|
|
29
|
-
│ ├─ catalog.mjs # canonical catalog loader
|
|
30
|
-
│ ├─ files.mjs # safe write/copy primitives
|
|
31
|
-
│ └─ paths.mjs # package-relative resolution
|
|
17
|
+
│ ├─ api/index.mjs # programmatic exports
|
|
18
|
+
│ ├─ cli/ # parsing, help, and routing
|
|
19
|
+
│ ├─ commands/ # setup, audit, worksheet, template, skill, validation
|
|
20
|
+
│ └─ core/ # catalog, config, filesystem, and package paths
|
|
32
21
|
├─ content/
|
|
33
|
-
│ ├─ catalog.json #
|
|
34
|
-
│ ├─
|
|
35
|
-
│
|
|
36
|
-
│
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
│
|
|
42
|
-
│
|
|
43
|
-
│
|
|
44
|
-
├─
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
│ │ ├─ engineering-report.md
|
|
51
|
-
│ │ └─ security-report.md
|
|
52
|
-
│ ├─ references/
|
|
53
|
-
│ │ ├─ documentation-governance.md
|
|
54
|
-
│ │ ├─ engineering-protocol.md
|
|
55
|
-
│ │ ├─ evidence-and-reporting.md
|
|
56
|
-
│ │ ├─ security-protocol.md
|
|
57
|
-
│ │ ├─ security-01-....md # 12 progressive phase files
|
|
58
|
-
│ │ └─ engineering-01-....md # 9 progressive topic files
|
|
59
|
-
│ ├─ scripts/
|
|
60
|
-
│ │ └─ check-report.mjs
|
|
61
|
-
│ └─ SKILL.md # concise routing instructions
|
|
62
|
-
├─ scripts/
|
|
63
|
-
│ ├─ build-site.mjs
|
|
64
|
-
│ ├─ build-skill.mjs
|
|
65
|
-
│ └─ validate-content.mjs
|
|
66
|
-
├─ site/
|
|
67
|
-
│ ├─ index.html
|
|
68
|
-
│ └─ styles.css
|
|
69
|
-
├─ tests/
|
|
70
|
-
│ ├─ cli.test.mjs
|
|
71
|
-
│ └─ content.test.mjs
|
|
72
|
-
├─ build/ # generated, ignored
|
|
73
|
-
│ └─ site/
|
|
74
|
-
├─ docs/
|
|
75
|
-
│ ├─ ARCHITECTURE.md
|
|
76
|
-
│ ├─ GITHUB_AND_NPM_SETUP.md
|
|
77
|
-
│ ├─ PURPOSE.md
|
|
78
|
-
│ ├─ RELEASE_PROCESS.md
|
|
79
|
-
│ └─ SECURITY_MODEL.md
|
|
80
|
-
├─ docs/archive/
|
|
81
|
-
│ └─ AGENT_DOCS_KIT_v3.2.md # retained legacy compiled edition
|
|
22
|
+
│ ├─ catalog.json # versioned selectors and review dates
|
|
23
|
+
│ ├─ documentation/ # 7 focused areas plus an index
|
|
24
|
+
│ ├─ security/ # 12 focused phases plus an index
|
|
25
|
+
│ └─ engineering/ # 14 focused topics plus an index
|
|
26
|
+
├─ templates/ # ADR, runbook, change, incident, and evidence records
|
|
27
|
+
├─ skills/manage-project-quality/
|
|
28
|
+
│ ├─ SKILL.md # progressive workflow router
|
|
29
|
+
│ ├─ agents/openai.yaml # skill UI metadata
|
|
30
|
+
│ ├─ assets/ # report templates
|
|
31
|
+
│ ├─ references/ # generated focused reference library
|
|
32
|
+
│ └─ scripts/check-report.mjs # report status/evidence validation
|
|
33
|
+
├─ scripts/ # build, validation, preview, and package smoke tests
|
|
34
|
+
├─ site/ # accessible static source
|
|
35
|
+
├─ tests/ # CLI, content, and safety regressions
|
|
36
|
+
├─ docs/ # maintainer and product documentation
|
|
37
|
+
├─ CHANGELOG.md
|
|
38
|
+
├─ SECURITY.md
|
|
82
39
|
├─ LICENSE
|
|
83
40
|
├─ README.md
|
|
84
41
|
└─ package.json
|
|
85
42
|
```
|
|
86
43
|
|
|
87
|
-
|
|
44
|
+
`content/` is the single canonical library for all three workflows. `agent-docs setup` creates `.agent-docs/config.json` and a project documentation tree. Reports are written beneath configured directories; the reference library stays in the npm package and installed skill rather than being duplicated into each project.
|
|
88
45
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```text
|
|
92
|
-
.agent-docs/config.json
|
|
93
|
-
docs/
|
|
94
|
-
├─ README.md
|
|
95
|
-
├─ 01-system/overview.md
|
|
96
|
-
├─ 02-project/README.md
|
|
97
|
-
├─ 03-development/
|
|
98
|
-
│ └─ engineering-reviews/
|
|
99
|
-
├─ 04-testing/
|
|
100
|
-
│ ├─ security-audits/
|
|
101
|
-
│ └─ evidence/
|
|
102
|
-
├─ 05-operations/README.md
|
|
103
|
-
├─ 06-future-enhancements/README.md
|
|
104
|
-
└─ archive/README.md
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
The CLI creates audit reports beneath this tree. It does not copy the entire reference library into each consumer project.
|
|
46
|
+
Generated outputs under `build/`, package archives, dependencies, and the maintainer’s local monthly AI prompt are ignored by Git.
|
|
@@ -6,4 +6,8 @@ Review security guidance at least quarterly and after material changes to major
|
|
|
6
6
|
|
|
7
7
|
Review engineering topics when supported runtimes, architectural assumptions, or operational scale change. Keep recommendations contextual and avoid presenting preferences as universal rules.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The catalog records `lastReviewed` and `reviewDue`. The scheduled health workflow fails after the due date; extending the date requires an actual content review.
|
|
10
|
+
|
|
11
|
+
Before release, run `npm run release:check`. Validation checks selectors, source files, duplicate headings, legacy references, encoding, package paths, templates, and website anchors. The smoke test installs the actual tarball into a clean temporary consumer. Human review is still required for technical accuracy and external-link freshness.
|
|
12
|
+
|
|
13
|
+
The ignored `MONTHLY_MAINTENANCE_AI.md` file is the maintainer's reusable AI runbook. It checks runtime support, dependencies, actions, dates, links, registry/tag/release alignment, and rotates one security phase plus one engineering topic each month.
|
|
@@ -102,7 +102,7 @@ In npm package settings, add a trusted publisher for GitHub Actions with these v
|
|
|
102
102
|
|
|
103
103
|
No npm token or GitHub environment is required. The workflow is restricted to this GitHub repository and workflow through OIDC.
|
|
104
104
|
|
|
105
|
-
After that, commit your changes, increment the npm version, and push
|
|
105
|
+
After that, commit your changes, increment the npm version, and push the matching version tag. The tag workflow verifies that the tag and `package.json` agree, runs the full release gate, publishes through npm trusted publishing, and creates the matching GitHub Release. A normal push to `main` updates source and Pages but does not publish npm. No long-lived `NPM_TOKEN` is required.
|
|
106
106
|
|
|
107
107
|
```powershell
|
|
108
108
|
npm.cmd version patch
|
package/docs/RELEASE_PROCESS.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Release process
|
|
2
2
|
|
|
3
|
-
Published npm versions are immutable. A push can publish a new version, but it cannot replace the contents of an existing version.
|
|
3
|
+
Published npm versions are immutable. A matching version-tag push can publish a new version, but it cannot replace the contents of an existing version.
|
|
4
4
|
|
|
5
5
|
1. Update canonical content and run `npm run build` to refresh progressive skill references and the site.
|
|
6
|
-
2. Run `npm
|
|
6
|
+
2. Run `npm run release:check`. This includes tests, validation, builds, a clean tarball installation, and `npm audit`.
|
|
7
7
|
3. Confirm the package archive contains no credentials, local reports, nested repositories, or generated consumer data.
|
|
8
8
|
4. Commit the package changes locally.
|
|
9
9
|
5. Update the version with `npm version patch`, `minor`, or `major` according to compatibility.
|
|
10
|
-
6. Push the commit and tag. A
|
|
11
|
-
7.
|
|
10
|
+
6. Push the commit and tag. A matching `v*` tag on `main` runs `.github/workflows/publish-npm.yml`, verifies the version and ancestry, and publishes through npm trusted publishing.
|
|
11
|
+
7. Let the workflow create the matching GitHub Release, then verify `npx --yes @ghostbyte/agent-docs-kit@<version> --help` from a clean directory.
|
|
12
12
|
|
|
13
13
|
```powershell
|
|
14
14
|
git add -A
|
|
@@ -17,6 +17,6 @@ npm.cmd version patch
|
|
|
17
17
|
git push --follow-tags
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Use `minor` for a backward-compatible feature and `major` for a breaking change.
|
|
20
|
+
Use `minor` for a backward-compatible feature and `major` for a breaking change. The workflow enforces agreement between `package.json` and the pushed tag, then creates the corresponding GitHub Release after publication.
|
|
21
21
|
|
|
22
22
|
The first release was published manually because the package had to exist before its trusted publisher could be configured. If a pushed version already exists, the workflow validates the project and safely skips publication; increment the version before pushing package changes that should reach npm.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostbyte/agent-docs-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Documentation governance, evidence-based security audits, and engineering-quality reviews for software projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
"templates/",
|
|
17
17
|
"skills/",
|
|
18
18
|
"docs/",
|
|
19
|
+
"scripts/",
|
|
20
|
+
"site/",
|
|
21
|
+
"tests/",
|
|
19
22
|
"README.md",
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"SECURITY.md",
|
|
20
25
|
"LICENSE"
|
|
21
26
|
],
|
|
22
27
|
"scripts": {
|
|
@@ -24,11 +29,14 @@
|
|
|
24
29
|
"site:preview": "node scripts/preview-site.mjs",
|
|
25
30
|
"doctor": "node bin/agent-docs.mjs doctor",
|
|
26
31
|
"test": "node --test",
|
|
27
|
-
"validate": "node scripts/validate-content.mjs && node bin/agent-docs.mjs validate",
|
|
28
|
-
"
|
|
32
|
+
"validate": "node scripts/validate-content.mjs && node scripts/check-links.mjs && node bin/agent-docs.mjs validate",
|
|
33
|
+
"smoke:package": "node scripts/smoke-package.mjs",
|
|
34
|
+
"review:dates": "node scripts/check-review-dates.mjs",
|
|
35
|
+
"release:check": "npm test && npm run validate && npm run review:dates && npm run build && npm run smoke:package && npm audit --cache .npm-cache",
|
|
36
|
+
"prepublishOnly": "npm run release:check"
|
|
29
37
|
},
|
|
30
38
|
"engines": {
|
|
31
|
-
"node": ">=
|
|
39
|
+
"node": ">=22"
|
|
32
40
|
},
|
|
33
41
|
"keywords": [
|
|
34
42
|
"ai-agent",
|
|
@@ -51,7 +59,6 @@
|
|
|
51
59
|
"access": "public",
|
|
52
60
|
"registry": "https://registry.npmjs.org/"
|
|
53
61
|
},
|
|
54
|
-
"main": "serve.js",
|
|
55
62
|
"directories": {
|
|
56
63
|
"doc": "docs",
|
|
57
64
|
"test": "tests"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
const root = resolve(import.meta.dirname, "..");
|
|
5
|
+
const output = resolve(root, "build/site");
|
|
6
|
+
await rm(output, { recursive: true, force: true });
|
|
7
|
+
await mkdir(output, { recursive: true });
|
|
8
|
+
await cp(resolve(root, "site"), output, { recursive: true });
|
|
9
|
+
await cp(resolve(root, "docs"), resolve(output, "docs"), { recursive: true });
|
|
10
|
+
await cp(resolve(root, "README.md"), resolve(output, "README.md"));
|
|
11
|
+
await cp(resolve(root, "LICENSE"), resolve(output, "LICENSE"));
|
|
12
|
+
await writeFile(resolve(output, ".nojekyll"), "", "utf8");
|
|
13
|
+
console.log(`Built site at ${output}`);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { copyFile, mkdir, readFile, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
const root = resolve(import.meta.dirname, "..");
|
|
5
|
+
const references = resolve(root, "skills/manage-project-quality/references");
|
|
6
|
+
await mkdir(references, { recursive: true });
|
|
7
|
+
const catalog = JSON.parse(await readFile(resolve(root, "content/catalog.json"), "utf8"));
|
|
8
|
+
const expected = new Set();
|
|
9
|
+
for (const kind of ["documentation", "security", "engineering"]) {
|
|
10
|
+
for (const entry of catalog[kind]) {
|
|
11
|
+
const name = `${kind}-${basename(entry.source)}`;
|
|
12
|
+
expected.add(name);
|
|
13
|
+
await copyFile(resolve(root, entry.source), resolve(references, name));
|
|
14
|
+
}
|
|
15
|
+
const fullName = `${kind}-full-reference.md`;
|
|
16
|
+
expected.add(fullName);
|
|
17
|
+
await copyFile(resolve(root, `content/${kind}/full-reference.md`), resolve(references, fullName));
|
|
18
|
+
}
|
|
19
|
+
for (const name of await readdir(references)) {
|
|
20
|
+
if (/^(?:documentation|security|engineering)-(?:\d{2}-.+|full-reference)\.md$/.test(name) && !expected.has(name)) {
|
|
21
|
+
await rm(resolve(references, name));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
console.log("Built progressive skill references.");
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, extname, relative, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
const root = resolve(import.meta.dirname, "..");
|
|
5
|
+
const excluded = new Set([".agents", ".codex", ".git", ".npm-cache", "build", "node_modules"]);
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
|
|
8
|
+
async function files(directory) {
|
|
9
|
+
const found = [];
|
|
10
|
+
for (const item of await readdir(directory, { withFileTypes: true })) {
|
|
11
|
+
if (item.isDirectory() && excluded.has(item.name)) continue;
|
|
12
|
+
const path = resolve(directory, item.name);
|
|
13
|
+
if (item.isDirectory()) found.push(...await files(path));
|
|
14
|
+
else if (item.isFile() && extname(path).toLowerCase() === ".md") found.push(path);
|
|
15
|
+
}
|
|
16
|
+
return found;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function slug(value) {
|
|
20
|
+
return value.toLowerCase().trim().replace(/[^\p{L}\p{N}\s-]/gu, "").replace(/\s+/g, "-");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const errors = [];
|
|
24
|
+
for (const file of await files(root)) {
|
|
25
|
+
const value = await readFile(file, "utf8");
|
|
26
|
+
cache.set(file, value);
|
|
27
|
+
const inspectable = value.replace(/^(```|~~~)[\s\S]*?^\1\s*$/gm, "");
|
|
28
|
+
for (const match of inspectable.matchAll(/\[[^\]]+\]\((<[^>]+>|[^\s)]+)(?:\s+["'][^"']*["'])?\)/g)) {
|
|
29
|
+
const raw = match[1].trim().replace(/^<|>$/g, "");
|
|
30
|
+
if (!raw || /^(?:https?:|mailto:)/i.test(raw)) continue;
|
|
31
|
+
const [pathPart, fragment] = raw.split("#", 2);
|
|
32
|
+
let decodedPath;
|
|
33
|
+
let decodedFragment;
|
|
34
|
+
try {
|
|
35
|
+
decodedPath = pathPart ? decodeURIComponent(pathPart) : "";
|
|
36
|
+
decodedFragment = fragment ? decodeURIComponent(fragment).toLowerCase() : "";
|
|
37
|
+
} catch {
|
|
38
|
+
errors.push(`${relative(root, file)} -> invalid URL encoding ${raw}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const target = decodedPath ? resolve(dirname(file), decodedPath) : file;
|
|
42
|
+
let targetValue;
|
|
43
|
+
try {
|
|
44
|
+
if (!cache.has(target)) cache.set(target, await readFile(target, "utf8"));
|
|
45
|
+
targetValue = cache.get(target);
|
|
46
|
+
}
|
|
47
|
+
catch { errors.push(`${relative(root, file)} -> missing ${raw}`); continue; }
|
|
48
|
+
if (fragment && extname(target).toLowerCase() === ".md") {
|
|
49
|
+
const anchors = new Set([...targetValue.matchAll(/^#{1,6}\s+(.+)$/gm)].map(([, heading]) => slug(heading)));
|
|
50
|
+
if (!anchors.has(decodedFragment)) errors.push(`${relative(root, file)} -> missing anchor ${raw}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (errors.length) { console.error(errors.join("\n")); process.exitCode = 1; }
|
|
56
|
+
else console.log("PASS internal Markdown links and anchors");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
const catalog = JSON.parse(await readFile(new URL("../content/catalog.json", import.meta.url), "utf8"));
|
|
4
|
+
const asOfFlag = process.argv.indexOf("--as-of");
|
|
5
|
+
const now = new Date();
|
|
6
|
+
const localToday = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
7
|
+
const today = asOfFlag >= 0 ? process.argv[asOfFlag + 1] : localToday;
|
|
8
|
+
const parsedToday = new Date(`${today}T00:00:00Z`);
|
|
9
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(today) || Number.isNaN(parsedToday.getTime()) || parsedToday.toISOString().slice(0, 10) !== today) throw new Error("Use a valid --as-of YYYY-MM-DD date.");
|
|
10
|
+
const overdue = [];
|
|
11
|
+
for (const kind of ["documentation", "security", "engineering"]) {
|
|
12
|
+
for (const entry of catalog[kind]) {
|
|
13
|
+
if (entry.reviewDue < today) overdue.push(`${kind}:${entry.id} ${entry.slug} (due ${entry.reviewDue})`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (overdue.length) {
|
|
17
|
+
console.error(`Review controls overdue as of ${today}:\n${overdue.join("\n")}`);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
} else {
|
|
20
|
+
console.log(`PASS review dates (${catalog.documentation.length + catalog.security.length + catalog.engineering.length} areas checked as of ${today})`);
|
|
21
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { extname, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
const root = resolve(import.meta.dirname, "../build/site");
|
|
7
|
+
const portIndex = process.argv.indexOf("--port");
|
|
8
|
+
const port = Number(portIndex >= 0 ? process.argv[portIndex + 1] : 4173);
|
|
9
|
+
const host = "127.0.0.1";
|
|
10
|
+
const types = {
|
|
11
|
+
".css": "text/css; charset=utf-8",
|
|
12
|
+
".html": "text/html; charset=utf-8",
|
|
13
|
+
".js": "text/javascript; charset=utf-8",
|
|
14
|
+
".json": "application/json; charset=utf-8",
|
|
15
|
+
".md": "text/markdown; charset=utf-8",
|
|
16
|
+
".svg": "image/svg+xml"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const server = createServer(async (request, response) => {
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? host}`);
|
|
22
|
+
const requested = decodeURIComponent(url.pathname === "/" ? "/index.html" : url.pathname);
|
|
23
|
+
const file = resolve(root, `.${requested}`);
|
|
24
|
+
if (file !== root && !file.startsWith(`${root}${sep}`)) {
|
|
25
|
+
response.writeHead(403).end("Forbidden");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const info = await stat(file);
|
|
29
|
+
if (!info.isFile()) throw new Error("Not a file");
|
|
30
|
+
response.writeHead(200, {
|
|
31
|
+
"Content-Type": types[extname(file)] ?? "application/octet-stream",
|
|
32
|
+
"Cache-Control": "no-store",
|
|
33
|
+
"X-Content-Type-Options": "nosniff"
|
|
34
|
+
});
|
|
35
|
+
createReadStream(file).pipe(response);
|
|
36
|
+
} catch {
|
|
37
|
+
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }).end("Not found");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
server.listen(port, host, () => {
|
|
42
|
+
console.log(`Agent Docs & Quality Kit preview: http://${host}:${port}`);
|
|
43
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
+
import { access, mkdtemp, mkdir, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
const root = resolve(import.meta.dirname, "..");
|
|
7
|
+
const scratch = await mkdtemp(resolve(tmpdir(), "agent-docs-kit-smoke-"));
|
|
8
|
+
const npmCli = process.env.npm_execpath;
|
|
9
|
+
const runNpm = (arguments_, options = {}) => npmCli
|
|
10
|
+
? execFileSync(process.execPath, [npmCli, ...arguments_], options)
|
|
11
|
+
: execFileSync("npm", arguments_, options);
|
|
12
|
+
try {
|
|
13
|
+
const cache = resolve(scratch, "npm-cache");
|
|
14
|
+
const packed = JSON.parse(runNpm(["pack", "--json", "--cache", cache, "--pack-destination", scratch], { cwd: root, encoding: "utf8" }));
|
|
15
|
+
const archivePaths = new Set(packed[0].files.map((entry) => entry.path.replaceAll("\\", "/")));
|
|
16
|
+
for (const required of ["scripts/validate-content.mjs", "site/index.html", "tests/cli.test.mjs", "skills/manage-project-quality/references/security-protocol.md", "skills/manage-project-quality/references/engineering-protocol.md", "skills/manage-project-quality/references/documentation-governance.md"]) {
|
|
17
|
+
if (!archivePaths.has(required)) throw new Error(`Packed archive is missing ${required}.`);
|
|
18
|
+
}
|
|
19
|
+
for (const path of archivePaths) {
|
|
20
|
+
if (/^(?:\.git|\.agent-docs|\.npm-cache|build|node_modules)(?:\/|$)|MONTHLY_MAINTENANCE_AI\.md$|\.tgz$/i.test(path)) throw new Error(`Private or generated path leaked into package: ${path}`);
|
|
21
|
+
}
|
|
22
|
+
const tarball = resolve(scratch, packed[0].filename);
|
|
23
|
+
const consumer = resolve(scratch, "consumer");
|
|
24
|
+
await mkdir(consumer);
|
|
25
|
+
runNpm(["init", "--yes", "--cache", cache], { cwd: consumer, stdio: "ignore" });
|
|
26
|
+
runNpm(["install", "--ignore-scripts", "--cache", cache, tarball], { cwd: consumer, stdio: "ignore" });
|
|
27
|
+
const packageRoot = resolve(consumer, "node_modules", "@ghostbyte", "agent-docs-kit");
|
|
28
|
+
const cli = resolve(packageRoot, "bin", "agent-docs.mjs");
|
|
29
|
+
const runCli = (...arguments_) => execFileSync(process.execPath, [cli, ...arguments_], { cwd: consumer, encoding: "utf8" });
|
|
30
|
+
const help = execFileSync(process.execPath, [cli, "--help"], { cwd: consumer, encoding: "utf8" });
|
|
31
|
+
if (!help.includes("documentation guide") || !help.includes("security worksheet") || !help.includes("template create")) throw new Error("Packed CLI help is incomplete.");
|
|
32
|
+
runCli("setup", "--dry-run");
|
|
33
|
+
runCli("setup");
|
|
34
|
+
runCli("audit", "documentation", "--strict", "--report", "--format", "json");
|
|
35
|
+
const securityOutput = runCli("security", "worksheet", "api-layer");
|
|
36
|
+
const engineeringOutput = runCli("engineering", "worksheet", "testing-types");
|
|
37
|
+
for (const output of [securityOutput, engineeringOutput]) {
|
|
38
|
+
const report = output.match(/created\s+(.+\.md)\s*$/m)?.[1];
|
|
39
|
+
if (!report) throw new Error(`Unable to locate generated report in output: ${output}`);
|
|
40
|
+
execFileSync(process.execPath, [resolve(packageRoot, "skills/manage-project-quality/scripts/check-report.mjs"), report], { cwd: consumer, stdio: "ignore" });
|
|
41
|
+
}
|
|
42
|
+
for (const type of ["adr", "runbook", "change", "incident", "evidence"]) runCli("template", "create", type, "--name", `smoke-${type}`);
|
|
43
|
+
runCli("documentation", "guide", "system-documentation");
|
|
44
|
+
runCli("skill", "install", "--target", "project");
|
|
45
|
+
runCli("validate");
|
|
46
|
+
await access(resolve(consumer, ".agents/skills/manage-project-quality/SKILL.md"));
|
|
47
|
+
execFileSync(process.execPath, ["--input-type=module", "--eval", "const api = await import('@ghostbyte/agent-docs-kit'); if (typeof api.auditDocumentation !== 'function' || typeof api.createTemplate !== 'function') process.exit(1);"], { cwd: consumer });
|
|
48
|
+
const invalid = spawnSync(process.execPath, [cli, "doctor", "unexpected"], { cwd: consumer, encoding: "utf8" });
|
|
49
|
+
if (invalid.status === 0 || !invalid.stderr.includes("Invalid arguments")) throw new Error("CLI accepted an unexpected positional argument.");
|
|
50
|
+
runNpm(["run", "validate"], { cwd: packageRoot, stdio: "ignore" });
|
|
51
|
+
const today = new Date();
|
|
52
|
+
const date = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
|
|
53
|
+
const reportBody = await readFile(resolve(consumer, "docs/03-development/audits", `${date}-documentation.json`), "utf8");
|
|
54
|
+
JSON.parse(reportBody);
|
|
55
|
+
console.log(`PASS packed installation (${packed[0].size} bytes, ${packed[0].entryCount} files)`);
|
|
56
|
+
} finally {
|
|
57
|
+
await rm(scratch, { recursive: true, force: true });
|
|
58
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { access, readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { basename, relative, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
const root = resolve(import.meta.dirname, "..");
|
|
5
|
+
const catalog = JSON.parse(await readFile(resolve(root, "content/catalog.json"), "utf8"));
|
|
6
|
+
const metadata = JSON.parse(await readFile(resolve(root, "package.json"), "utf8"));
|
|
7
|
+
const errors = [];
|
|
8
|
+
const identities = new Set();
|
|
9
|
+
const expectedGenerated = new Set();
|
|
10
|
+
const aliases = { documentation: new Map(), security: new Map(), engineering: new Map() };
|
|
11
|
+
const controlIds = new Set();
|
|
12
|
+
|
|
13
|
+
function validDate(value) {
|
|
14
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value ?? "")) return false;
|
|
15
|
+
const parsed = new Date(`${value}T00:00:00Z`);
|
|
16
|
+
return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (catalog.schemaVersion !== 2) errors.push("content/catalog.json must use schemaVersion 2.");
|
|
20
|
+
if (!validDate(catalog.lastReviewed)) errors.push("Catalog lastReviewed is missing or invalid.");
|
|
21
|
+
if (!validDate(catalog.reviewDue)) errors.push("Catalog reviewDue is missing or invalid.");
|
|
22
|
+
if (catalog.reviewDue < catalog.lastReviewed) errors.push("Catalog reviewDue precedes lastReviewed.");
|
|
23
|
+
|
|
24
|
+
for (const kind of ["documentation", "security", "engineering"]) {
|
|
25
|
+
for (const entry of catalog[kind]) {
|
|
26
|
+
const identity = `${kind}:${entry.id}:${entry.slug}`;
|
|
27
|
+
if (identities.has(identity)) errors.push(`Duplicate catalog entry: ${identity}`);
|
|
28
|
+
identities.add(identity);
|
|
29
|
+
if (!validDate(entry.lastReviewed)) errors.push(`${identity} has an invalid lastReviewed date.`);
|
|
30
|
+
if (!validDate(entry.reviewDue)) errors.push(`${identity} has an invalid reviewDue date.`);
|
|
31
|
+
if (entry.reviewDue < entry.lastReviewed) errors.push(`${identity} reviewDue precedes lastReviewed.`);
|
|
32
|
+
for (const selector of new Set([entry.id, entry.slug, ...(entry.aliases ?? [])])) {
|
|
33
|
+
if (aliases[kind].has(selector) && aliases[kind].get(selector) !== identity) errors.push(`Duplicate ${kind} selector: ${selector}`);
|
|
34
|
+
aliases[kind].set(selector, identity);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const value = await readFile(resolve(root, entry.source), "utf8");
|
|
38
|
+
const h1s = value.match(/^#\s+/gm) ?? [];
|
|
39
|
+
if (h1s.length !== 1) errors.push(`${entry.source} must contain exactly one H1; found ${h1s.length}.`);
|
|
40
|
+
if (/\uFFFD|Ã|â(?:€|†)/.test(value)) errors.push(`${entry.source} contains encoding artifacts.`);
|
|
41
|
+
if (/CHECKLIST\.md|RESULTS_TEMPLATE\.md|SECURITY_CHECKLIST\.md|ENGINEERING_REFERENCE\.md|security-gate/.test(value)) errors.push(`${entry.source} contains a legacy path reference.`);
|
|
42
|
+
if (/Files in this folder|this same folder|RESULTS_\d{4}-\d{2}-\d{2}|Reference (?:checklist|list) only/.test(value)) errors.push(`${entry.source} contains legacy folder-oriented instructions.`);
|
|
43
|
+
const prefix = kind === "documentation" ? "DOC" : kind === "security" ? "SEC" : "ENG";
|
|
44
|
+
const ids = value.match(new RegExp(`\\b${prefix}-${entry.id}-\\d{2}\\b`, "g")) ?? [];
|
|
45
|
+
if (!ids.length) errors.push(`${entry.source} has no stable ${prefix}-${entry.id}-NN identifiers.`);
|
|
46
|
+
for (const id of ids) {
|
|
47
|
+
if (controlIds.has(id)) errors.push(`Duplicate control identifier: ${id}`);
|
|
48
|
+
controlIds.add(id);
|
|
49
|
+
}
|
|
50
|
+
const generated = resolve(root, "skills/manage-project-quality/references", `${kind}-${basename(entry.source)}`);
|
|
51
|
+
expectedGenerated.add(basename(generated));
|
|
52
|
+
try {
|
|
53
|
+
if (await readFile(generated, "utf8") !== value) errors.push(`Generated skill reference is stale: ${relative(root, generated)}`);
|
|
54
|
+
} catch { errors.push(`Missing generated skill reference: ${relative(root, generated)}`); }
|
|
55
|
+
} catch { errors.push(`Missing source: ${entry.source}`); }
|
|
56
|
+
}
|
|
57
|
+
const fullSource = resolve(root, `content/${kind}/full-reference.md`);
|
|
58
|
+
const fullGenerated = resolve(root, `skills/manage-project-quality/references/${kind}-full-reference.md`);
|
|
59
|
+
expectedGenerated.add(basename(fullGenerated));
|
|
60
|
+
try {
|
|
61
|
+
if (await readFile(fullSource, "utf8") !== await readFile(fullGenerated, "utf8")) errors.push(`Generated ${kind} full reference is stale.`);
|
|
62
|
+
} catch { errors.push(`Missing ${kind} full reference or generated copy.`); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const name of await readdir(resolve(root, "skills/manage-project-quality/references"))) {
|
|
66
|
+
if (/^(?:documentation|security|engineering)-(?:\d{2}-.+|full-reference)\.md$/.test(name) && !expectedGenerated.has(name)) errors.push(`Unexpected generated skill reference: ${name}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (catalog.documentation.length !== 7) errors.push(`Expected 7 documentation areas; found ${catalog.documentation.length}.`);
|
|
70
|
+
if (catalog.security.length !== 12) errors.push(`Expected 12 security phases; found ${catalog.security.length}.`);
|
|
71
|
+
if (catalog.engineering.length !== 14) errors.push(`Expected 14 engineering topics; found ${catalog.engineering.length}.`);
|
|
72
|
+
if (!Number(metadata.engines?.node?.match(/\d+/)?.[0]) || Number(metadata.engines.node.match(/\d+/)[0]) < 22) errors.push("Node.js engine must require 22 or newer.");
|
|
73
|
+
for (const directory of metadata.files ?? []) {
|
|
74
|
+
try { await access(resolve(root, directory)); }
|
|
75
|
+
catch { errors.push(`package.json files entry does not exist: ${directory}`); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const site = await readFile(resolve(root, "site/index.html"), "utf8");
|
|
79
|
+
const ids = [...site.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
|
|
80
|
+
const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index);
|
|
81
|
+
for (const id of new Set(duplicateIds)) errors.push(`Duplicate website id: ${id}`);
|
|
82
|
+
for (const [, anchor] of site.matchAll(/href="#([^"]+)"/g)) if (!ids.includes(anchor)) errors.push(`Broken website anchor: #${anchor}`);
|
|
83
|
+
|
|
84
|
+
if (errors.length) { console.error(errors.join("\n")); process.exitCode = 1; }
|
|
85
|
+
else console.log(`Validated ${identities.size} catalog entries, package paths, and website anchors.`);
|