ai-project-maintainer 0.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/ai-project-maintainer/SKILL.md +62 -0
  4. package/ai-project-maintainer/agents/openai.yaml +6 -0
  5. package/ai-project-maintainer/references/ci-guardrails.md +55 -0
  6. package/ai-project-maintainer/references/database.md +60 -0
  7. package/ai-project-maintainer/references/electron-desktop.md +43 -0
  8. package/ai-project-maintainer/references/incident-response.md +52 -0
  9. package/ai-project-maintainer/references/local-gate.md +117 -0
  10. package/ai-project-maintainer/references/security.md +48 -0
  11. package/ai-project-maintainer/references/tool-router.md +53 -0
  12. package/ai-project-maintainer/scripts/audit-plan.mjs +155 -0
  13. package/ai-project-maintainer/scripts/bootstrap-local-tools.ps1 +109 -0
  14. package/ai-project-maintainer/scripts/check-syntax.mjs +41 -0
  15. package/ai-project-maintainer/scripts/ci-smoke-gate.mjs +26 -0
  16. package/ai-project-maintainer/scripts/cli.mjs +165 -0
  17. package/ai-project-maintainer/scripts/doctor.mjs +80 -0
  18. package/ai-project-maintainer/scripts/init-audit.mjs +105 -0
  19. package/ai-project-maintainer/scripts/init-project.mjs +229 -0
  20. package/ai-project-maintainer/scripts/lib/check-registry.mjs +68 -0
  21. package/ai-project-maintainer/scripts/lib/checks.mjs +337 -0
  22. package/ai-project-maintainer/scripts/lib/command-runner.mjs +130 -0
  23. package/ai-project-maintainer/scripts/lib/intake.mjs +172 -0
  24. package/ai-project-maintainer/scripts/lib/policy.mjs +150 -0
  25. package/ai-project-maintainer/scripts/lib/project-detect.mjs +111 -0
  26. package/ai-project-maintainer/scripts/lib/report.mjs +227 -0
  27. package/ai-project-maintainer/scripts/probe-project.mjs +218 -0
  28. package/ai-project-maintainer/scripts/report-summary.mjs +25 -0
  29. package/ai-project-maintainer/scripts/run-local-gate.mjs +147 -0
  30. package/docs/CI-GITHUB-ACTIONS.zh-CN.md +83 -0
  31. package/docs/DEMO.md +81 -0
  32. package/docs/DEMO.zh-CN.md +81 -0
  33. package/docs/GITHUB-LAUNCH-CHECKLIST.md +77 -0
  34. package/docs/INSTALL.zh-CN.md +112 -0
  35. package/docs/INTAKE-SCHEMA.zh-CN.md +105 -0
  36. package/docs/POLICY-AND-EXCEPTIONS.zh-CN.md +96 -0
  37. package/docs/PRODUCTION-AUDIT.zh-CN.md +89 -0
  38. package/docs/PROMOTION.md +116 -0
  39. package/docs/UPGRADE-ROADMAP.zh-CN.md +47 -0
  40. package/docs/demo-output/security-report.md +57 -0
  41. package/docs/superpowers/plans/2026-06-29-ci-dogfooding.md +200 -0
  42. package/package.json +21 -0
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import YAML from "yaml";
6
+ import {
7
+ defaultBusinessFlows,
8
+ defaultEvidenceSources,
9
+ defaultProjectProfile,
10
+ defaultRiskPolicy,
11
+ } from "./lib/intake.mjs";
12
+
13
+ const markdownTemplates = {
14
+ ".ai-maintainer/threat-model.md": `# Threat Model
15
+
16
+ ## Assets
17
+
18
+ - Source code
19
+ - User data
20
+ - Credentials and local secrets
21
+
22
+ ## Trust Boundaries
23
+
24
+ - Browser or renderer process
25
+ - API or main process
26
+ - Database and file system
27
+ - CI/CD and release artifacts
28
+
29
+ ## User Decisions
30
+
31
+ - Confirm the real roles, permissions, and sensitive data before production review.
32
+ `,
33
+ ".ai-maintainer/release-checklist.yml": `schema_version: 1
34
+ release:
35
+ has_staging: false
36
+ has_smoke_tests: false
37
+ has_manual_approval: false
38
+ has_rollback_plan: false
39
+ has_versioned_artifacts: false
40
+ `,
41
+ ".ai-maintainer/incident-runbook.md": `# Incident Runbook
42
+
43
+ ## First Response
44
+
45
+ - Identify current version and latest deploy.
46
+ - Check error monitoring, logs, and recent migrations.
47
+ - Decide whether rollback is safe.
48
+
49
+ ## User Decisions
50
+
51
+ - Define who can approve rollback and user communication.
52
+ `,
53
+ ".ai-maintainer/db-migration-policy.yml": `schema_version: 1
54
+ database:
55
+ changes_use_migrations: true
56
+ destructive_changes_require_review: true
57
+ backup_before_production_migration: false
58
+ rollback_or_forward_fix_required: false
59
+ `,
60
+ ".ai-maintainer/observability-checklist.yml": `schema_version: 1
61
+ observability:
62
+ error_monitoring: false
63
+ structured_logs: false
64
+ metrics: false
65
+ alerts: false
66
+ release_tracking: false
67
+ `,
68
+ };
69
+
70
+ function yamlTemplate(value) {
71
+ return YAML.stringify(value);
72
+ }
73
+
74
+ function safeWrite(root, relativePath, content, result) {
75
+ const full = path.join(root, ...relativePath.split("/"));
76
+ fs.mkdirSync(path.dirname(full), { recursive: true });
77
+ if (fs.existsSync(full)) {
78
+ result.skipped.push(relativePath);
79
+ return;
80
+ }
81
+ fs.writeFileSync(full, content);
82
+ result.created.push(relativePath);
83
+ }
84
+
85
+ export function initAudit(projectRoot) {
86
+ const root = path.resolve(projectRoot || process.cwd());
87
+ const result = { root, created: [], skipped: [] };
88
+
89
+ safeWrite(root, ".ai-maintainer/project-profile.yml", yamlTemplate(defaultProjectProfile), result);
90
+ safeWrite(root, ".ai-maintainer/evidence-sources.yml", yamlTemplate(defaultEvidenceSources), result);
91
+ safeWrite(root, ".ai-maintainer/business-flows.yml", yamlTemplate(defaultBusinessFlows), result);
92
+ safeWrite(root, ".ai-maintainer/risk-policy.yml", yamlTemplate(defaultRiskPolicy), result);
93
+ for (const [relativePath, content] of Object.entries(markdownTemplates)) safeWrite(root, relativePath, content, result);
94
+
95
+ return result;
96
+ }
97
+
98
+ function main() {
99
+ const projectRoot = process.argv.slice(2).find((arg) => !arg.startsWith("--")) || process.cwd();
100
+ console.log(JSON.stringify(initAudit(projectRoot), null, 2));
101
+ }
102
+
103
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
104
+ main();
105
+ }
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const policyTemplate = `profile: oss
7
+ mode: strict
8
+ checks:
9
+ gitleaks: block
10
+ trivy: block
11
+ semgrep: block
12
+ osv-scanner: warn
13
+ syft: warn
14
+ grype: warn
15
+ actionlint: block
16
+ zizmor: warn
17
+ checkov: warn
18
+ trivy-config: warn
19
+ scorecard: warn
20
+ megalinter: warn
21
+ pre-commit: warn
22
+ fail_on:
23
+ tests: true
24
+ secrets: true
25
+ dependency_high_or_critical: true
26
+ semgrep_blocking: true
27
+ trivy_unavailable: true
28
+ electron_dangerous_settings: true
29
+ ci_security_high: true
30
+ warn_on:
31
+ dev_dependency_vulnerabilities: true
32
+ missing_optional_tools: true
33
+ `;
34
+
35
+ const exceptionsTemplate = `exceptions:
36
+ - id: "example-dev-only-vuln"
37
+ check: "npm audit"
38
+ reason: "dev-only transitive dependency, not shipped"
39
+ expires: "2026-09-01"
40
+ owner: "repo-owner"
41
+ `;
42
+
43
+ const workflowTemplate = `name: Security Gate
44
+
45
+ on:
46
+ pull_request:
47
+ push:
48
+ branches:
49
+ - main
50
+ - master
51
+
52
+ permissions:
53
+ contents: read
54
+ security-events: write
55
+ actions: read
56
+
57
+ jobs:
58
+ security-gate:
59
+ runs-on: ubuntu-latest
60
+ steps:
61
+ - name: Checkout project
62
+ uses: actions/checkout@v4
63
+
64
+ - name: Setup Node
65
+ uses: actions/setup-node@v4
66
+ with:
67
+ node-version: "22"
68
+
69
+ - name: Setup Go for scanner CLIs
70
+ uses: actions/setup-go@v5
71
+ with:
72
+ go-version: "1.23"
73
+
74
+ - name: Install project dependencies
75
+ shell: bash
76
+ run: |
77
+ set -euo pipefail
78
+ if [ -f pnpm-lock.yaml ]; then
79
+ corepack enable
80
+ pnpm install --frozen-lockfile
81
+ elif [ -f yarn.lock ]; then
82
+ corepack enable
83
+ yarn install --immutable || yarn install --frozen-lockfile
84
+ elif [ -f bun.lock ] || [ -f bun.lockb ]; then
85
+ curl -fsSL https://bun.sh/install | bash
86
+ echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
87
+ "$HOME/.bun/bin/bun" install --frozen-lockfile
88
+ elif [ -f package-lock.json ]; then
89
+ npm ci
90
+ elif [ -f package.json ]; then
91
+ npm install
92
+ fi
93
+
94
+ - name: Install security scanners
95
+ shell: bash
96
+ run: |
97
+ set -euo pipefail
98
+ mkdir -p "$HOME/.local/bin"
99
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
100
+ echo "$HOME/go/bin" >> "$GITHUB_PATH"
101
+ python -m pip install --user semgrep zizmor checkov
102
+ go install github.com/gitleaks/gitleaks/v8@latest
103
+ go install github.com/google/osv-scanner/cmd/osv-scanner@latest
104
+ go install github.com/rhysd/actionlint/cmd/actionlint@latest
105
+ go install github.com/anchore/syft/cmd/syft@latest
106
+ go install github.com/anchore/grype/cmd/grype@latest
107
+ curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b "$HOME/.local/bin"
108
+
109
+ - name: Checkout AI Project Maintainer
110
+ shell: bash
111
+ run: |
112
+ set -euo pipefail
113
+ git clone --depth 1 https://github.com/xixifusi1213-gif/ai-project-maintainer.git "$RUNNER_TEMP/ai-project-maintainer"
114
+ cd "$RUNNER_TEMP/ai-project-maintainer"
115
+ npm ci --omit=dev || npm install --omit=dev
116
+
117
+ - name: Run security gate
118
+ shell: bash
119
+ run: |
120
+ set -euo pipefail
121
+ EXTRA_FLAGS=""
122
+ if [ -f ".ai-maintainer/project-profile.yml" ]; then
123
+ EXTRA_FLAGS="$EXTRA_FLAGS --production"
124
+ fi
125
+ node "$RUNNER_TEMP/ai-project-maintainer/ai-project-maintainer/scripts/run-local-gate.mjs" "$GITHUB_WORKSPACE" --strict --release $EXTRA_FLAGS --output reports/security-report.json
126
+
127
+ - name: Write gate summary
128
+ if: always()
129
+ shell: bash
130
+ run: |
131
+ if [ -f reports/security-report.md ]; then
132
+ cat reports/security-report.md >> "$GITHUB_STEP_SUMMARY"
133
+ fi
134
+
135
+ - name: Upload SARIF to code scanning
136
+ if: always()
137
+ uses: github/codeql-action/upload-sarif@v3
138
+ with:
139
+ sarif_file: reports/security-report.sarif
140
+ continue-on-error: true
141
+
142
+ - name: Upload security reports
143
+ if: always()
144
+ uses: actions/upload-artifact@v4
145
+ with:
146
+ name: security-reports
147
+ path: |
148
+ reports/security-report.json
149
+ reports/security-report.md
150
+ reports/security-report.sarif
151
+ reports/sbom.cdx.json
152
+ if-no-files-found: ignore
153
+ `;
154
+
155
+ const dependabotTemplate = `version: 2
156
+ updates:
157
+ - package-ecosystem: "github-actions"
158
+ directory: "/"
159
+ schedule:
160
+ interval: "weekly"
161
+ - package-ecosystem: "npm"
162
+ directory: "/"
163
+ schedule:
164
+ interval: "weekly"
165
+ `;
166
+
167
+ const preCommitTemplate = `repos:
168
+ - repo: local
169
+ hooks:
170
+ - id: ai-project-maintainer-local-gate
171
+ name: AI Project Maintainer local gate
172
+ entry: npx ai-project-maintainer gate .
173
+ language: system
174
+ pass_filenames: false
175
+ `;
176
+
177
+ function safeWrite(root, relativePath, content, result) {
178
+ const full = path.join(root, ...relativePath.split("/"));
179
+ fs.mkdirSync(path.dirname(full), { recursive: true });
180
+ if (fs.existsSync(full)) {
181
+ result.skipped.push(relativePath);
182
+ return;
183
+ }
184
+ fs.writeFileSync(full, content);
185
+ result.created.push(relativePath);
186
+ }
187
+
188
+ export function initProject(projectRoot, options = {}) {
189
+ const root = path.resolve(projectRoot || process.cwd());
190
+ const result = { root, created: [], skipped: [] };
191
+
192
+ safeWrite(root, ".ai-maintainer/policy.yml", policyTemplate, result);
193
+ safeWrite(root, ".ai-maintainer/exceptions.yml", exceptionsTemplate, result);
194
+ if ((options.ci || "github") === "github") {
195
+ safeWrite(root, ".github/workflows/security-gate.yml", workflowTemplate, result);
196
+ safeWrite(root, ".github/dependabot.yml", dependabotTemplate, result);
197
+ }
198
+ safeWrite(root, "reports/.gitkeep", "", result);
199
+ if (options.preCommit) {
200
+ safeWrite(root, ".pre-commit-config.yaml", preCommitTemplate, result);
201
+ }
202
+
203
+ return result;
204
+ }
205
+
206
+ function parseArgs(args) {
207
+ const readOption = (name, fallback) => {
208
+ const index = args.indexOf(name);
209
+ if (index !== -1) return args[index + 1] || fallback;
210
+ const inline = args.find((arg) => arg.startsWith(`${name}=`));
211
+ return inline ? inline.slice(name.length + 1) : fallback;
212
+ };
213
+ return {
214
+ projectRoot: args.find((arg) => !arg.startsWith("--")) || process.cwd(),
215
+ profile: readOption("--profile", "oss"),
216
+ ci: readOption("--ci", "github"),
217
+ preCommit: args.includes("--pre-commit"),
218
+ };
219
+ }
220
+
221
+ function main() {
222
+ const args = parseArgs(process.argv.slice(2));
223
+ const result = initProject(args.projectRoot, { profile: args.profile, ci: args.ci, preCommit: args.preCommit });
224
+ console.log(JSON.stringify(result, null, 2));
225
+ }
226
+
227
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
228
+ main();
229
+ }
@@ -0,0 +1,68 @@
1
+ import {
2
+ runCiSecurityChecks,
3
+ runDatabaseChecks,
4
+ runElectronChecks,
5
+ runGrypeChecks,
6
+ runIacChecks,
7
+ runMegaLinterChecks,
8
+ runOsvScannerChecks,
9
+ runPackageAuditChecks,
10
+ runPreCommitChecks,
11
+ runSastChecks,
12
+ runScorecardChecks,
13
+ runSecretChecks,
14
+ runSyftChecks,
15
+ runTestChecks,
16
+ runTrivyFilesystemChecks,
17
+ } from "./checks.mjs";
18
+
19
+ const builtinCheckRegistry = [
20
+ { id: "tests", group: "tests", title: "Project tests and release scripts", requiredTools: ["npm", "pnpm", "yarn", "bun"], detect: (project) => Boolean(project.packageJson), run: ({ project, options }) => runTestChecks(project, options), defaultLevel: "block" },
21
+ { id: "package-audit", group: "dependencies", title: "Package manager production audit", requiredTools: ["npm", "pnpm", "yarn"], detect: (project) => Boolean(project.packageJson), run: ({ project, options }) => runPackageAuditChecks(project, options), defaultLevel: "block" },
22
+ { id: "gitleaks", group: "secrets", title: "Gitleaks secret scan", requiredTools: ["gitleaks"], detect: () => true, run: ({ project, options }) => runSecretChecks(project, options), defaultLevel: "block" },
23
+ { id: "trivy", group: "dependencies", title: "Trivy filesystem scan", requiredTools: ["trivy"], detect: () => true, run: ({ project, options }) => runTrivyFilesystemChecks(project, options), defaultLevel: "block" },
24
+ { id: "osv-scanner", group: "dependencies", title: "OSV-Scanner dependency scan", requiredTools: ["osv-scanner"], detect: (project) => (project.files || []).some((file) => /(^|\/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?|go\.sum|requirements.*\.txt|poetry\.lock|Cargo\.lock|Gemfile\.lock)$/i.test(file)), run: ({ project, options }) => runOsvScannerChecks(project, options), defaultLevel: "warn" },
25
+ { id: "semgrep", group: "sast", title: "Semgrep static scan", requiredTools: ["semgrep"], detect: () => true, run: ({ project, options }) => runSastChecks(project, options), defaultLevel: "block" },
26
+ { id: "syft", group: "supply-chain", title: "Syft SBOM", requiredTools: ["syft"], detect: () => true, run: ({ project, options }) => runSyftChecks(project, options), defaultLevel: "warn" },
27
+ { id: "grype", group: "supply-chain", title: "Grype vulnerability scan", requiredTools: ["grype"], detect: () => true, run: ({ project, options }) => runGrypeChecks(project, options), defaultLevel: "warn" },
28
+ { id: "actionlint", group: "ci-security", title: "actionlint workflow lint", requiredTools: ["actionlint"], detect: (project) => (project.riskSurfaces?.ci || []).length > 0, run: ({ project, options }) => runCiSecurityChecks(project, options).filter((check) => check.checkId === "actionlint"), defaultLevel: "block" },
29
+ { id: "zizmor", group: "ci-security", title: "zizmor workflow security", requiredTools: ["zizmor"], detect: (project) => (project.riskSurfaces?.ci || []).length > 0, run: ({ project, options }) => runCiSecurityChecks(project, options).filter((check) => check.checkId === "zizmor"), defaultLevel: "warn" },
30
+ { id: "checkov", group: "iac", title: "Checkov IaC scan", requiredTools: ["checkov"], detect: (project) => (project.riskSurfaces?.infra || []).length > 0, run: ({ project, options }) => runIacChecks(project, options).filter((check) => check.checkId === "checkov" || check.checkId === "trivy-config"), defaultLevel: "warn" },
31
+ { id: "electron", group: "electron", title: "Electron baseline", requiredTools: [], detect: (project) => Boolean(project.electron?.detected), run: ({ project }) => runElectronChecks(project), defaultLevel: "block" },
32
+ { id: "database", group: "database", title: "Database migration review", requiredTools: ["squawk"], detect: (project) => (project.riskSurfaces?.database || []).length > 0, run: ({ project, options }) => runDatabaseChecks(project, options), defaultLevel: "block" },
33
+ { id: "scorecard", group: "oss-hygiene", title: "OpenSSF Scorecard", requiredTools: ["scorecard"], detect: () => true, run: ({ project, options }) => runScorecardChecks(project, options), defaultLevel: "warn" },
34
+ { id: "pre-commit", group: "oss-hygiene", title: "pre-commit hooks", requiredTools: ["pre-commit"], detect: () => true, run: ({ project, options }) => runPreCommitChecks(project, options), defaultLevel: "warn" },
35
+ { id: "megalinter", group: "oss-hygiene", title: "MegaLinter security profile", requiredTools: ["mega-linter-runner"], detect: () => true, run: ({ project, options }) => runMegaLinterChecks(project, options), defaultLevel: "warn" },
36
+ ];
37
+
38
+ export function getBuiltinCheckRegistry() {
39
+ return builtinCheckRegistry.map((check) => ({ ...check }));
40
+ }
41
+
42
+ export function resolveEnabledChecks(registry, policy = {}) {
43
+ const configuredLevels = policy.checks || {};
44
+ return registry
45
+ .map((check) => ({ ...check, level: configuredLevels[check.id] || check.defaultLevel }))
46
+ .filter((check) => check.level !== "off");
47
+ }
48
+
49
+ export function runRegisteredChecks(project, options = {}) {
50
+ const policy = options.policy || {};
51
+ const registry = options.registry || getBuiltinCheckRegistry();
52
+ const enabledChecks = resolveEnabledChecks(registry, policy);
53
+ const results = [];
54
+
55
+ for (const definition of enabledChecks) {
56
+ if (!definition.detect(project, options)) continue;
57
+ const checks = definition.run({ project, options, definition });
58
+ for (const check of checks) {
59
+ results.push({
60
+ checkId: check.checkId || definition.id,
61
+ policyLevel: definition.level,
62
+ ...check,
63
+ });
64
+ }
65
+ }
66
+
67
+ return results;
68
+ }