@ghostbyte/agent-docs-kit 4.0.3 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +43 -38
  3. package/SECURITY.md +11 -0
  4. package/content/catalog.json +191 -22
  5. package/content/documentation/01-system-documentation.md +13 -0
  6. package/content/documentation/02-project-governance.md +13 -0
  7. package/content/documentation/03-development-records.md +13 -0
  8. package/content/documentation/04-testing-evidence.md +13 -0
  9. package/content/documentation/05-operations-runbooks.md +13 -0
  10. package/content/documentation/06-future-proposals.md +13 -0
  11. package/content/documentation/07-archive-governance.md +13 -0
  12. package/content/documentation/full-reference.md +24 -0
  13. package/content/engineering/01-query-performance.md +17 -27
  14. package/content/engineering/02-networking-fundamentals.md +17 -27
  15. package/content/engineering/03-rate-limiting-algorithms.md +18 -28
  16. package/content/engineering/04-production-debugging.md +17 -27
  17. package/content/engineering/05-code-review-practices.md +17 -27
  18. package/content/engineering/06-data-modeling.md +17 -27
  19. package/content/engineering/07-cloud-cost-saving.md +17 -27
  20. package/content/engineering/08-testing-types.md +17 -27
  21. package/content/engineering/09-message-queues.md +17 -27
  22. package/content/engineering/10-api-design-compatibility.md +14 -0
  23. package/content/engineering/11-reliability-slos.md +14 -0
  24. package/content/engineering/12-observability-telemetry.md +14 -0
  25. package/content/engineering/13-deployment-change-safety.md +14 -0
  26. package/content/engineering/14-capacity-concurrency.md +14 -0
  27. package/content/engineering/full-reference.md +24 -144
  28. package/content/security/01-design-architecture.md +11 -19
  29. package/content/security/02-local-development.md +13 -21
  30. package/content/security/03-database-backend.md +18 -26
  31. package/content/security/04-api-layer.md +23 -31
  32. package/content/security/05-frontend-ui.md +16 -24
  33. package/content/security/06-application-security-testing.md +22 -28
  34. package/content/security/07-cicd-release-engineering.md +13 -21
  35. package/content/security/08-cloud-infrastructure.md +17 -25
  36. package/content/security/09-edge-cdn-dns.md +16 -24
  37. package/content/security/10-release-rollout.md +13 -21
  38. package/content/security/11-post-release-monitoring-ir.md +15 -23
  39. package/content/security/12-ongoing-operations.md +24 -32
  40. package/content/security/full-reference.md +30 -3163
  41. package/docs/ARCHITECTURE.md +32 -91
  42. package/docs/CONTENT_MAINTENANCE.md +5 -1
  43. package/docs/GITHUB_AND_NPM_SETUP.md +1 -1
  44. package/docs/RELEASE_PROCESS.md +6 -6
  45. package/docs/SECURITY_MODEL.md +2 -0
  46. package/package.json +20 -7
  47. package/scripts/build-site.mjs +13 -0
  48. package/scripts/build-skill.mjs +24 -0
  49. package/scripts/check-links.mjs +56 -0
  50. package/scripts/check-review-dates.mjs +21 -0
  51. package/scripts/preview-site.mjs +43 -0
  52. package/scripts/smoke-package.mjs +82 -0
  53. package/scripts/validate-content.mjs +97 -0
  54. package/site/app.js +121 -0
  55. package/site/index.html +346 -0
  56. package/site/styles.css +159 -0
  57. package/skills/manage-project-quality/SKILL.md +18 -2
  58. package/skills/manage-project-quality/assets/documentation-report.md +3 -3
  59. package/skills/manage-project-quality/assets/engineering-report.md +3 -3
  60. package/skills/manage-project-quality/assets/security-report.md +3 -3
  61. package/skills/manage-project-quality/references/documentation-01-system-documentation.md +13 -0
  62. package/skills/manage-project-quality/references/documentation-02-project-governance.md +13 -0
  63. package/skills/manage-project-quality/references/documentation-03-development-records.md +13 -0
  64. package/skills/manage-project-quality/references/documentation-04-testing-evidence.md +13 -0
  65. package/skills/manage-project-quality/references/documentation-05-operations-runbooks.md +13 -0
  66. package/skills/manage-project-quality/references/documentation-06-future-proposals.md +13 -0
  67. package/skills/manage-project-quality/references/documentation-07-archive-governance.md +13 -0
  68. package/skills/manage-project-quality/references/documentation-full-reference.md +24 -0
  69. package/skills/manage-project-quality/references/documentation-governance.md +2 -2
  70. package/skills/manage-project-quality/references/engineering-01-query-performance.md +17 -27
  71. package/skills/manage-project-quality/references/engineering-02-networking-fundamentals.md +17 -27
  72. package/skills/manage-project-quality/references/engineering-03-rate-limiting-algorithms.md +18 -28
  73. package/skills/manage-project-quality/references/engineering-04-production-debugging.md +17 -27
  74. package/skills/manage-project-quality/references/engineering-05-code-review-practices.md +17 -27
  75. package/skills/manage-project-quality/references/engineering-06-data-modeling.md +17 -27
  76. package/skills/manage-project-quality/references/engineering-07-cloud-cost-saving.md +17 -27
  77. package/skills/manage-project-quality/references/engineering-08-testing-types.md +17 -27
  78. package/skills/manage-project-quality/references/engineering-09-message-queues.md +17 -27
  79. package/skills/manage-project-quality/references/engineering-10-api-design-compatibility.md +14 -0
  80. package/skills/manage-project-quality/references/engineering-11-reliability-slos.md +14 -0
  81. package/skills/manage-project-quality/references/engineering-12-observability-telemetry.md +14 -0
  82. package/skills/manage-project-quality/references/engineering-13-deployment-change-safety.md +14 -0
  83. package/skills/manage-project-quality/references/engineering-14-capacity-concurrency.md +14 -0
  84. package/skills/manage-project-quality/references/engineering-full-reference.md +24 -144
  85. package/skills/manage-project-quality/references/engineering-protocol.md +1 -1
  86. package/skills/manage-project-quality/references/security-01-design-architecture.md +11 -19
  87. package/skills/manage-project-quality/references/security-02-local-development.md +13 -21
  88. package/skills/manage-project-quality/references/security-03-database-backend.md +18 -26
  89. package/skills/manage-project-quality/references/security-04-api-layer.md +23 -31
  90. package/skills/manage-project-quality/references/security-05-frontend-ui.md +16 -24
  91. package/skills/manage-project-quality/references/security-06-application-security-testing.md +22 -28
  92. package/skills/manage-project-quality/references/security-07-cicd-release-engineering.md +13 -21
  93. package/skills/manage-project-quality/references/security-08-cloud-infrastructure.md +17 -25
  94. package/skills/manage-project-quality/references/security-09-edge-cdn-dns.md +16 -24
  95. package/skills/manage-project-quality/references/security-10-release-rollout.md +13 -21
  96. package/skills/manage-project-quality/references/security-11-post-release-monitoring-ir.md +15 -23
  97. package/skills/manage-project-quality/references/security-12-ongoing-operations.md +24 -32
  98. package/skills/manage-project-quality/references/security-full-reference.md +30 -3163
  99. package/skills/manage-project-quality/scripts/check-report.mjs +67 -16
  100. package/src/api/index.mjs +3 -0
  101. package/src/cli/arguments.mjs +27 -3
  102. package/src/cli/help.mjs +37 -17
  103. package/src/cli/main.mjs +64 -10
  104. package/src/commands/audit.mjs +146 -31
  105. package/src/commands/doctor.mjs +1 -1
  106. package/src/commands/guide.mjs +12 -0
  107. package/src/commands/list.mjs +1 -1
  108. package/src/commands/setup.mjs +9 -4
  109. package/src/commands/skill.mjs +1 -0
  110. package/src/commands/template.mjs +32 -0
  111. package/src/commands/validate.mjs +36 -4
  112. package/src/core/config.mjs +61 -0
  113. package/src/core/files.mjs +68 -6
  114. package/templates/adr.md +20 -0
  115. package/templates/change-record.md +18 -0
  116. package/templates/incident-review.md +20 -0
  117. package/templates/runbook.md +18 -0
  118. package/templates/test-evidence.md +18 -0
  119. package/tests/cli.test.mjs +242 -0
  120. package/tests/content.test.mjs +18 -0
@@ -1,107 +1,48 @@
1
1
  # Architecture and file structure
2
2
 
3
- The repository separates executable behavior, canonical reference content, the installable skill, project templates, maintainer automation, tests, and the static website.
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 # tests, validation, package smoke check
10
- │ ├─ pages.yml # GitHub Pages build and deployment
11
- │ └─ publish-npm.yml # npm trusted-publishing release
12
- ├─ bin/
13
- │ └─ agent-docs.mjs # npm executable entry point
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
- │ │ └─ index.mjs # programmatic public exports
17
- │ ├─ cli/
18
- │ │ ├─ arguments.mjs # option and positional parsing
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 # phase/topic IDs, slugs, sources
34
- │ ├─ security/
35
- │ │ ├─ 01-design-architecture.md
36
- │ │ ├─ ... # phases 02–11
37
- │ │ ├─ 12-ongoing-operations.md
38
- │ │ └─ full-reference.md # deep lookup; not loaded by default
39
- │ └─ engineering/
40
- │ ├─ 01-query-performance.md
41
- │ ├─ ... # topics 02–08
42
- │ ├─ 09-message-queues.md
43
- │ └─ full-reference.md
44
- ├─ skills/
45
- │ └─ manage-project-quality/
46
- │ ├─ agents/
47
- │ │ └─ openai.yaml # skill UI metadata
48
- │ ├─ assets/
49
- │ │ ├─ documentation-report.md
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
- ## Installed project structure
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
- `agent-docs setup` creates only the starting documents needed to establish ownership and navigation:
46
+ Generated outputs under `build/`, package archives, dependencies, and the maintainer’s local monthly AI prompt are ignored by Git. Local consumer-style documentation under the configured `docs/` workspace is also ignored in this repository.
90
47
 
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.
48
+ Git ignore rules are not treated as a publishing boundary. `package.json` explicitly lists the six public maintainer guides, `docs/.npmignore` excludes local workspace records, and the packed-installation smoke test rejects every unexpected `docs/` path and every missing declared entry point.
@@ -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
- Before release, run the content validator. It checks catalog/source consistency and common encoding corruption. A human review is still required for technical accuracy and link freshness.
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 to `main`. The push automatically validates and publishes the new version. No long-lived `NPM_TOKEN` is required.
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
@@ -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 test`, `npm run validate`, and `npm pack --dry-run`.
7
- 3. Confirm the package archive contains no credentials, local reports, nested repositories, or generated consumer data.
6
+ 2. Run `npm run release:check`. This includes tests, validation, builds, a clean tarball installation, and `npm audit`.
7
+ 3. Confirm the package archive contains no credentials, local reports, nested repositories, or generated consumer data. `npm pack --dry-run --json` must show only the six approved public files under `docs/`, and every `main`, `bin`, and `exports` target must exist in the archive.
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 push to `main` runs `.github/workflows/publish-npm.yml` and publishes the new version through npm trusted publishing.
11
- 7. Verify `npx --yes @ghostbyte/agent-docs-kit@<version> --help` from a clean directory.
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. A GitHub Release is optional and does not control npm publication.
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.
@@ -4,6 +4,8 @@ The CLI is inert on installation: `package.json` defines no `preinstall`, `insta
4
4
 
5
5
  Project setup skips existing files by default. `--force` is required to replace files owned by a command, and `--dry-run` previews writes. The package does not install Git hooks automatically, execute network scans, read credentials, or contact a telemetry service.
6
6
 
7
+ Forced scaffold replacement is staged in the destination directory before the owned file is swapped into place. npm packaging uses an explicit public-document allowlist plus a packed-manifest check; local documentation, generated findings, and audit evidence are rejected from the archive even when they exist in the working tree.
8
+
7
9
  Audit reports begin as `Unverified`. The tool scaffolds the review; it does not claim that generated text proves a control. Agents and reviewers must attach repository paths, line numbers, reproducible command output, or authorized external evidence.
8
10
 
9
11
  Publishing uses npm provenance through GitHub trusted publishing when configured. Protect the GitHub release environment, require reviews as appropriate, and never store a long-lived npm token when OIDC is available.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostbyte/agent-docs-kit",
3
- "version": "4.0.3",
3
+ "version": "5.0.1",
4
4
  "description": "Documentation governance, evidence-based security audits, and engineering-quality reviews for software projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,8 +15,18 @@
15
15
  "content/",
16
16
  "templates/",
17
17
  "skills/",
18
- "docs/",
18
+ "docs/ARCHITECTURE.md",
19
+ "docs/CONTENT_MAINTENANCE.md",
20
+ "docs/GITHUB_AND_NPM_SETUP.md",
21
+ "docs/PURPOSE.md",
22
+ "docs/RELEASE_PROCESS.md",
23
+ "docs/SECURITY_MODEL.md",
24
+ "scripts/",
25
+ "site/",
26
+ "tests/",
19
27
  "README.md",
28
+ "CHANGELOG.md",
29
+ "SECURITY.md",
20
30
  "LICENSE"
21
31
  ],
22
32
  "scripts": {
@@ -24,11 +34,14 @@
24
34
  "site:preview": "node scripts/preview-site.mjs",
25
35
  "doctor": "node bin/agent-docs.mjs doctor",
26
36
  "test": "node --test",
27
- "validate": "node scripts/validate-content.mjs && node bin/agent-docs.mjs validate",
28
- "prepublishOnly": "npm run build && npm test && npm run validate"
37
+ "validate": "node scripts/validate-content.mjs && node scripts/check-links.mjs && node bin/agent-docs.mjs validate",
38
+ "smoke:package": "node scripts/smoke-package.mjs",
39
+ "review:dates": "node scripts/check-review-dates.mjs",
40
+ "release:check": "npm test && npm run validate && npm run review:dates && npm run build && npm run smoke:package && npm audit --cache .npm-cache",
41
+ "prepublishOnly": "npm run release:check"
29
42
  },
30
43
  "engines": {
31
- "node": ">=20"
44
+ "node": ">=22"
32
45
  },
33
46
  "keywords": [
34
47
  "ai-agent",
@@ -51,9 +64,9 @@
51
64
  "access": "public",
52
65
  "registry": "https://registry.npmjs.org/"
53
66
  },
54
- "main": "serve.js",
55
67
  "directories": {
56
68
  "doc": "docs",
57
69
  "test": "tests"
58
- }
70
+ },
71
+ "main": "./src/api/index.mjs"
59
72
  }
@@ -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,82 @@
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
+ const publicDocs = new Set([
17
+ "docs/ARCHITECTURE.md",
18
+ "docs/CONTENT_MAINTENANCE.md",
19
+ "docs/GITHUB_AND_NPM_SETUP.md",
20
+ "docs/PURPOSE.md",
21
+ "docs/RELEASE_PROCESS.md",
22
+ "docs/SECURITY_MODEL.md"
23
+ ]);
24
+ 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"]) {
25
+ if (!archivePaths.has(required)) throw new Error(`Packed archive is missing ${required}.`);
26
+ }
27
+ for (const publicDoc of publicDocs) {
28
+ if (!archivePaths.has(publicDoc)) throw new Error(`Packed archive is missing public documentation: ${publicDoc}`);
29
+ }
30
+ for (const path of archivePaths) {
31
+ 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}`);
32
+ if (path.startsWith("docs/") && !publicDocs.has(path)) throw new Error(`Local documentation leaked into package: ${path}`);
33
+ }
34
+ const packageMetadata = JSON.parse(await readFile(resolve(root, "package.json"), "utf8"));
35
+ const declaredTargets = new Set([packageMetadata.main, ...Object.values(packageMetadata.bin ?? {})]);
36
+ const collectExportTargets = (value) => {
37
+ if (typeof value === "string") declaredTargets.add(value);
38
+ else if (Array.isArray(value)) value.forEach(collectExportTargets);
39
+ else if (value && typeof value === "object") Object.values(value).forEach(collectExportTargets);
40
+ };
41
+ collectExportTargets(packageMetadata.exports);
42
+ for (const target of declaredTargets) {
43
+ const normalized = target?.replace(/^\.\//, "").replaceAll("\\", "/");
44
+ if (normalized && !archivePaths.has(normalized)) throw new Error(`Declared package entry point is missing from the archive: ${target}`);
45
+ }
46
+ const tarball = resolve(scratch, packed[0].filename);
47
+ const consumer = resolve(scratch, "consumer");
48
+ await mkdir(consumer);
49
+ runNpm(["init", "--yes", "--cache", cache], { cwd: consumer, stdio: "ignore" });
50
+ runNpm(["install", "--ignore-scripts", "--cache", cache, tarball], { cwd: consumer, stdio: "ignore" });
51
+ const packageRoot = resolve(consumer, "node_modules", "@ghostbyte", "agent-docs-kit");
52
+ const cli = resolve(packageRoot, "bin", "agent-docs.mjs");
53
+ const runCli = (...arguments_) => execFileSync(process.execPath, [cli, ...arguments_], { cwd: consumer, encoding: "utf8" });
54
+ const help = execFileSync(process.execPath, [cli, "--help"], { cwd: consumer, encoding: "utf8" });
55
+ if (!help.includes("documentation guide") || !help.includes("security worksheet") || !help.includes("template create")) throw new Error("Packed CLI help is incomplete.");
56
+ runCli("setup", "--dry-run");
57
+ runCli("setup");
58
+ runCli("audit", "documentation", "--strict", "--report", "--format", "json");
59
+ const securityOutput = runCli("security", "worksheet", "api-layer");
60
+ const engineeringOutput = runCli("engineering", "worksheet", "testing-types");
61
+ for (const output of [securityOutput, engineeringOutput]) {
62
+ const report = output.match(/created\s+(.+\.md)\s*$/m)?.[1];
63
+ if (!report) throw new Error(`Unable to locate generated report in output: ${output}`);
64
+ execFileSync(process.execPath, [resolve(packageRoot, "skills/manage-project-quality/scripts/check-report.mjs"), report], { cwd: consumer, stdio: "ignore" });
65
+ }
66
+ for (const type of ["adr", "runbook", "change", "incident", "evidence"]) runCli("template", "create", type, "--name", `smoke-${type}`);
67
+ runCli("documentation", "guide", "system-documentation");
68
+ runCli("skill", "install", "--target", "project");
69
+ runCli("validate");
70
+ await access(resolve(consumer, ".agents/skills/manage-project-quality/SKILL.md"));
71
+ 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 });
72
+ const invalid = spawnSync(process.execPath, [cli, "doctor", "unexpected"], { cwd: consumer, encoding: "utf8" });
73
+ if (invalid.status === 0 || !invalid.stderr.includes("Invalid arguments")) throw new Error("CLI accepted an unexpected positional argument.");
74
+ runNpm(["run", "validate"], { cwd: packageRoot, stdio: "ignore" });
75
+ const today = new Date();
76
+ const date = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
77
+ const reportBody = await readFile(resolve(consumer, "docs/03-development/audits", `${date}-documentation.json`), "utf8");
78
+ JSON.parse(reportBody);
79
+ console.log(`PASS packed installation (${packed[0].size} bytes, ${packed[0].entryCount} files)`);
80
+ } finally {
81
+ await rm(scratch, { recursive: true, force: true });
82
+ }
@@ -0,0 +1,97 @@
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
+ const declaredTargets = new Set([metadata.main, ...Object.values(metadata.bin ?? {})]);
78
+ const collectExportTargets = (value) => {
79
+ if (typeof value === "string") declaredTargets.add(value);
80
+ else if (Array.isArray(value)) value.forEach(collectExportTargets);
81
+ else if (value && typeof value === "object") Object.values(value).forEach(collectExportTargets);
82
+ };
83
+ collectExportTargets(metadata.exports);
84
+ for (const target of declaredTargets) {
85
+ if (!target) continue;
86
+ try { await access(resolve(root, target)); }
87
+ catch { errors.push(`Declared package entry point does not exist: ${target}`); }
88
+ }
89
+
90
+ const site = await readFile(resolve(root, "site/index.html"), "utf8");
91
+ const ids = [...site.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
92
+ const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index);
93
+ for (const id of new Set(duplicateIds)) errors.push(`Duplicate website id: ${id}`);
94
+ for (const [, anchor] of site.matchAll(/href="#([^"]+)"/g)) if (!ids.includes(anchor)) errors.push(`Broken website anchor: #${anchor}`);
95
+
96
+ if (errors.length) { console.error(errors.join("\n")); process.exitCode = 1; }
97
+ else console.log(`Validated ${identities.size} catalog entries, package paths, and website anchors.`);