pi-evalset-lab 0.1.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 (63) hide show
  1. package/.copier-answers.yml +5 -0
  2. package/.githooks/pre-commit +12 -0
  3. package/.github/CODEOWNERS +12 -0
  4. package/.github/ISSUE_TEMPLATE/bug-report.yml +63 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. package/.github/ISSUE_TEMPLATE/docs.yml +39 -0
  7. package/.github/ISSUE_TEMPLATE/feature-request.yml +41 -0
  8. package/.github/VOUCHED.td +8 -0
  9. package/.github/dependabot.yml +13 -0
  10. package/.github/pull_request_template.md +34 -0
  11. package/.github/workflows/ci.yml +37 -0
  12. package/.github/workflows/publish.yml +60 -0
  13. package/.github/workflows/release-please.yml +25 -0
  14. package/.github/workflows/vouch-check-pr.yml +29 -0
  15. package/.github/workflows/vouch-manage.yml +34 -0
  16. package/.pi/extensions/startup-intake-router.ts +151 -0
  17. package/.pi/prompts/init-project-docs.md +32 -0
  18. package/.release-please-config.json +11 -0
  19. package/.release-please-manifest.json +3 -0
  20. package/AGENTS.md +39 -0
  21. package/CHANGELOG.md +43 -0
  22. package/CODE_OF_CONDUCT.md +50 -0
  23. package/CONTRIBUTING.md +28 -0
  24. package/NEXT_SESSION_PROMPT.md +14 -0
  25. package/README.md +246 -0
  26. package/SECURITY.md +34 -0
  27. package/SUPPORT.md +37 -0
  28. package/docs/dev/CONTRIBUTING.md +37 -0
  29. package/docs/dev/EXTENSION_SOP.md +43 -0
  30. package/docs/dev/next_steps.md +17 -0
  31. package/docs/dev/plans/001-initial-plan.md +24 -0
  32. package/docs/dev/status.md +21 -0
  33. package/docs/org/operating_model.md +39 -0
  34. package/docs/org/project-docs-intake.questions.json +60 -0
  35. package/docs/project/foundation.md +28 -0
  36. package/docs/project/incentives.md +17 -0
  37. package/docs/project/resources.md +26 -0
  38. package/docs/project/skills.md +17 -0
  39. package/docs/project/strategic_goals.md +18 -0
  40. package/docs/project/tactical_goals.md +39 -0
  41. package/docs/project/vision.md +21 -0
  42. package/examples/.gitkeep +0 -0
  43. package/examples/fixed-task-set-v2.json +127 -0
  44. package/examples/fixed-task-set-v3.json +126 -0
  45. package/examples/fixed-task-set.json +22 -0
  46. package/examples/system-baseline.txt +1 -0
  47. package/examples/system-candidate.txt +6 -0
  48. package/extensions/evalset.ts +1090 -0
  49. package/external/.gitkeep +0 -0
  50. package/ontology/.gitkeep +0 -0
  51. package/package.json +31 -0
  52. package/policy/security-policy.json +10 -0
  53. package/prek.toml +15 -0
  54. package/prompts/implementation-planning.md +17 -0
  55. package/prompts/init-project-docs.md +32 -0
  56. package/prompts/security-review.md +17 -0
  57. package/scripts/docs-list.sh +50 -0
  58. package/scripts/init-project-docs.sh +56 -0
  59. package/scripts/install-hooks.sh +13 -0
  60. package/scripts/sync-to-live.sh +91 -0
  61. package/scripts/validate-structure.sh +325 -0
  62. package/src/.gitkeep +0 -0
  63. package/tests/.gitkeep +0 -0
File without changes
File without changes
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "pi-evalset-lab",
3
+ "version": "0.1.0",
4
+ "description": "pi extension package: pi-evalset-lab",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": ["pi-package"],
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/tryingET/pi-evalset-lab.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/tryingET/pi-evalset-lab/issues"
14
+ },
15
+ "homepage": "https://github.com/tryingET/pi-evalset-lab#readme",
16
+ "scripts": {
17
+ "check": "bash ./scripts/validate-structure.sh",
18
+ "test": "bash ./scripts/validate-structure.sh",
19
+ "docs:list": "bash ./scripts/docs-list.sh",
20
+ "docs:list:workspace": "bash ./scripts/docs-list.sh --workspace --discover",
21
+ "docs:list:json": "bash ./scripts/docs-list.sh --json"
22
+ },
23
+ "pi": {
24
+ "extensions": ["./extensions/evalset.ts"],
25
+ "prompts": ["./prompts"]
26
+ },
27
+ "peerDependencies": {
28
+ "@mariozechner/pi-coding-agent": "*",
29
+ "@mariozechner/pi-ai": "*"
30
+ }
31
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "default": {
3
+ "maxRiskScore": 70,
4
+ "requireIntegrity": true,
5
+ "requireSignatures": false,
6
+ "allowNpmDiffFallback": false,
7
+ "minReleaseAgeHours": 0
8
+ },
9
+ "packages": {}
10
+ }
package/prek.toml ADDED
@@ -0,0 +1,15 @@
1
+ minimum_prek_version = "0.3.0"
2
+
3
+ [[repos]]
4
+ repo = "local"
5
+ hooks = [
6
+ {
7
+ id = "validate-structure",
8
+ name = "validate structure",
9
+ language = "system",
10
+ entry = "bash ./scripts/validate-structure.sh",
11
+ pass_filenames = false,
12
+ always_run = true,
13
+ stages = ["pre-commit"]
14
+ }
15
+ ]
@@ -0,0 +1,17 @@
1
+ ---
2
+ description: Draft an implementation plan for a requested change
3
+ system4d:
4
+ container: "Prompt template for implementation planning."
5
+ compass: "Turn requests into actionable, risk-aware plans."
6
+ engine: "Scope -> tasks -> validation -> rollout."
7
+ fog: "Hidden constraints unless assumptions are surfaced."
8
+ ---
9
+
10
+ Create an implementation plan for this request: $@
11
+
12
+ Include:
13
+ - Scope and non-goals
14
+ - Key risks and mitigations
15
+ - Step-by-step implementation tasks
16
+ - Validation commands and expected outcomes
17
+ - Rollout and rollback notes
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Run interview-first initialization for organization and project docs
3
+ system4d:
4
+ container: "Prompt template for document initialization workflow."
5
+ compass: "Create compact, aligned org/project docs from structured intake."
6
+ engine: "Intent -> interview -> synthesize -> update docs -> verify."
7
+ fog: "Incomplete intake answers can cause ambiguous documentation."
8
+ ---
9
+
10
+ Initialize organization and project docs from interactive intake.
11
+
12
+ Startup intent (if provided): $@
13
+
14
+ Steps:
15
+ 1. Read `docs/org/project-docs-intake.questions.json`.
16
+ 2. If startup intent is non-empty, create `docs/org/project-docs-intake.runtime.questions.json` with one prepended question:
17
+ - `id`: `startup_intent_confirmation`
18
+ - `type`: `text`
19
+ - `question`: `Startup intent captured: <startup intent>. Confirm or refine this intent before continuing.`
20
+ 3. Run the `interview` tool:
21
+ - `questions`: runtime file from step 2 if created, otherwise `docs/org/project-docs-intake.questions.json`
22
+ - `timeout`: `900`
23
+ 4. Use interview responses to update these files:
24
+ - `docs/org/operating_model.md`
25
+ - `docs/project/foundation.md`
26
+ - `docs/project/vision.md`
27
+ - `docs/project/strategic_goals.md`
28
+ - `docs/project/tactical_goals.md`
29
+ 5. Keep wording fully in English.
30
+ 6. Keep **organization purpose** separate from **project purpose**.
31
+ 7. Keep output compact.
32
+ 8. Run `bash ./scripts/validate-structure.sh`.
@@ -0,0 +1,17 @@
1
+ ---
2
+ description: Review a change for security risks and mitigations
3
+ system4d:
4
+ container: "Prompt template for security-focused review."
5
+ compass: "Identify practical vulnerabilities before release."
6
+ engine: "Threats -> impact -> mitigations -> verification."
7
+ fog: "Partial context can hide exploit paths."
8
+ ---
9
+
10
+ Review this change for security concerns: $@
11
+
12
+ Focus on:
13
+ - Input validation and injection risk
14
+ - Privilege boundaries and secret handling
15
+ - Dependency and supply-chain risk
16
+ - Safe failure modes and logging
17
+ - Concrete remediations with priority
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ DEFAULT_SCRIPT="$HOME/ai-society/core/agent-scripts/scripts/docs-list.mjs"
6
+ LOCAL_FALLBACK_SCRIPT="$ROOT_DIR/scripts/docs-list.mjs"
7
+
8
+ usage() {
9
+ cat <<'USAGE'
10
+ Usage: ./scripts/docs-list.sh [docs-list args]
11
+
12
+ Resolves docs-list script in this order:
13
+ 1) DOCS_LIST_SCRIPT env var (absolute/relative path)
14
+ 2) Local fallback: ./scripts/docs-list.mjs
15
+ 3) Default global path: ~/ai-society/core/agent-scripts/scripts/docs-list.mjs
16
+
17
+ Examples:
18
+ ./scripts/docs-list.sh
19
+ ./scripts/docs-list.sh --workspace --discover
20
+ ./scripts/docs-list.sh --json
21
+ USAGE
22
+ }
23
+
24
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
25
+ usage
26
+ exit 0
27
+ fi
28
+
29
+ if ! command -v node >/dev/null 2>&1; then
30
+ echo "Error: node is required to run docs-list." >&2
31
+ exit 1
32
+ fi
33
+
34
+ SCRIPT_PATH=""
35
+
36
+ if [[ -n "${DOCS_LIST_SCRIPT:-}" ]]; then
37
+ SCRIPT_PATH="${DOCS_LIST_SCRIPT}"
38
+ elif [[ -f "$LOCAL_FALLBACK_SCRIPT" ]]; then
39
+ SCRIPT_PATH="$LOCAL_FALLBACK_SCRIPT"
40
+ else
41
+ SCRIPT_PATH="$DEFAULT_SCRIPT"
42
+ fi
43
+
44
+ if [[ ! -f "$SCRIPT_PATH" ]]; then
45
+ echo "Error: docs-list script not found: $SCRIPT_PATH" >&2
46
+ echo "Set DOCS_LIST_SCRIPT to your docs-list.mjs path, or install agent-scripts at: $DEFAULT_SCRIPT" >&2
47
+ exit 1
48
+ fi
49
+
50
+ node "$SCRIPT_PATH" "$@"
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ QUESTIONS_FILE="$ROOT_DIR/docs/org/project-docs-intake.questions.json"
6
+ ROUTER_FILE="$ROOT_DIR/.pi/extensions/startup-intake-router.ts"
7
+ PROMPT_FILE="$ROOT_DIR/.pi/prompts/init-project-docs.md"
8
+
9
+ INSTALL_INTERVIEW=false
10
+
11
+ while [[ $# -gt 0 ]]; do
12
+ case "$1" in
13
+ --install-interview)
14
+ INSTALL_INTERVIEW=true
15
+ ;;
16
+ -h|--help)
17
+ echo "Usage: ./scripts/init-project-docs.sh [--install-interview]"
18
+ exit 0
19
+ ;;
20
+ *)
21
+ echo "Unknown arg: $1" >&2
22
+ echo "Usage: ./scripts/init-project-docs.sh [--install-interview]" >&2
23
+ exit 1
24
+ ;;
25
+ esac
26
+ shift
27
+ done
28
+
29
+ for required in "$QUESTIONS_FILE" "$ROUTER_FILE" "$PROMPT_FILE"; do
30
+ if [[ ! -f "$required" ]]; then
31
+ echo "Missing required file: $required" >&2
32
+ exit 1
33
+ fi
34
+ done
35
+
36
+ if [[ "$INSTALL_INTERVIEW" == "true" ]]; then
37
+ if command -v pi-interview >/dev/null 2>&1; then
38
+ pi-interview
39
+ else
40
+ echo "pi-interview command not found. Install the interactive-interview extension first, then rerun with --install-interview." >&2
41
+ fi
42
+ fi
43
+
44
+ echo "Interview-first document setup"
45
+ echo ""
46
+ echo "0) Optional: run npm run docs:list to review docs + read_when hints."
47
+ echo "1) Ensure the interview extension is installed and loaded in pi."
48
+ echo "2) Open this repository in pi."
49
+ echo "3) Send a natural-language intent as the first non-command message in a session."
50
+ echo "4) Router will prefill: /init-project-docs \"<intent>\""
51
+ echo "5) Review/edit and run that command."
52
+ echo ""
53
+ echo "Fallback: run /init-project-docs manually."
54
+ echo "Questions source: $QUESTIONS_FILE"
55
+ echo ""
56
+ echo "After updates, run: bash ./scripts/validate-structure.sh"
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+
6
+ git -C "$ROOT_DIR" config core.hooksPath .githooks
7
+ echo "Configured git hooks path: .githooks"
8
+
9
+ if command -v prek >/dev/null 2>&1; then
10
+ echo "prek detected: pre-commit hook will run prek.toml"
11
+ else
12
+ echo "prek not found: pre-commit hook will fallback to scripts/validate-structure.sh"
13
+ fi
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ WITH_POLICY=false
5
+ WITH_PROMPTS=false
6
+
7
+ usage() {
8
+ cat <<'USAGE'
9
+ Usage: ./scripts/sync-to-live.sh [--with-policy] [--with-prompts] [--all]
10
+
11
+ Copies all package extension entrypoints from ./extensions into ~/.pi/agent/extensions/.
12
+ Optional flags also sync policy and prompt templates.
13
+ USAGE
14
+ }
15
+
16
+ while [[ $# -gt 0 ]]; do
17
+ case "$1" in
18
+ --with-policy)
19
+ WITH_POLICY=true
20
+ ;;
21
+ --with-prompts)
22
+ WITH_PROMPTS=true
23
+ ;;
24
+ --all)
25
+ WITH_POLICY=true
26
+ WITH_PROMPTS=true
27
+ ;;
28
+ -h|--help)
29
+ usage
30
+ exit 0
31
+ ;;
32
+ *)
33
+ echo "Unknown arg: $1" >&2
34
+ usage
35
+ exit 1
36
+ ;;
37
+ esac
38
+ shift
39
+ done
40
+
41
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
42
+ SOURCE_DIR="$ROOT_DIR/extensions"
43
+ TARGET_DIR="$HOME/.pi/agent/extensions"
44
+
45
+ mkdir -p "$TARGET_DIR"
46
+
47
+ shopt -s nullglob
48
+ extension_files=("$SOURCE_DIR"/*.ts)
49
+ if (( ${#extension_files[@]} == 0 )); then
50
+ echo "No extension files found in: $SOURCE_DIR" >&2
51
+ exit 1
52
+ fi
53
+
54
+ for source_file in "${extension_files[@]}"; do
55
+ target_file="$TARGET_DIR/$(basename "$source_file")"
56
+ cp "$source_file" "$target_file"
57
+ echo "Synced extension: $source_file -> $target_file"
58
+ done
59
+ shopt -u nullglob
60
+
61
+ if [[ "$WITH_PROMPTS" == "true" ]]; then
62
+ PROMPT_SOURCE_DIR="$ROOT_DIR/prompts"
63
+ PROMPT_TARGET_DIR="$HOME/.pi/agent/prompts"
64
+ mkdir -p "$PROMPT_TARGET_DIR"
65
+
66
+ shopt -s nullglob
67
+ prompt_files=("$PROMPT_SOURCE_DIR"/*.md)
68
+ if (( ${#prompt_files[@]} == 0 )); then
69
+ echo "No prompt templates found in: $PROMPT_SOURCE_DIR"
70
+ else
71
+ for prompt_file in "${prompt_files[@]}"; do
72
+ cp "$prompt_file" "$PROMPT_TARGET_DIR/"
73
+ echo "Synced prompt: $prompt_file -> $PROMPT_TARGET_DIR/$(basename "$prompt_file")"
74
+ done
75
+ fi
76
+ shopt -u nullglob
77
+ fi
78
+
79
+ if [[ "$WITH_POLICY" == "true" ]]; then
80
+ POLICY_SOURCE="$ROOT_DIR/policy/security-policy.json"
81
+ POLICY_TARGET="$HOME/.pi/agent/security-policy.json"
82
+
83
+ if [[ -f "$POLICY_SOURCE" ]]; then
84
+ cp "$POLICY_SOURCE" "$POLICY_TARGET"
85
+ echo "Synced policy: $POLICY_SOURCE -> $POLICY_TARGET"
86
+ else
87
+ echo "Policy file not found: $POLICY_SOURCE (skipped)"
88
+ fi
89
+ fi
90
+
91
+ echo "Done. In pi, run /reload to pick up changes."
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "$ROOT_DIR"
6
+
7
+ required_files=(
8
+ "README.md"
9
+ "CHANGELOG.md"
10
+ "SECURITY.md"
11
+ "CODE_OF_CONDUCT.md"
12
+ "SUPPORT.md"
13
+ "CONTRIBUTING.md"
14
+ "AGENTS.md"
15
+ ".copier-answers.yml"
16
+ "prek.toml"
17
+ ".github/CODEOWNERS"
18
+ ".github/dependabot.yml"
19
+ ".github/pull_request_template.md"
20
+ ".github/VOUCHED.td"
21
+ ".github/ISSUE_TEMPLATE/bug-report.yml"
22
+ ".github/ISSUE_TEMPLATE/feature-request.yml"
23
+ ".github/ISSUE_TEMPLATE/docs.yml"
24
+ ".github/ISSUE_TEMPLATE/config.yml"
25
+ ".github/workflows/ci.yml"
26
+ ".github/workflows/release-please.yml"
27
+ ".github/workflows/publish.yml"
28
+ ".github/workflows/vouch-check-pr.yml"
29
+ ".github/workflows/vouch-manage.yml"
30
+ ".release-please-config.json"
31
+ ".release-please-manifest.json"
32
+ "docs/org/operating_model.md"
33
+ "docs/org/project-docs-intake.questions.json"
34
+ "docs/project/foundation.md"
35
+ "docs/project/vision.md"
36
+ "docs/project/incentives.md"
37
+ "docs/project/resources.md"
38
+ "docs/project/skills.md"
39
+ "docs/project/strategic_goals.md"
40
+ "docs/project/tactical_goals.md"
41
+ "docs/dev/next_steps.md"
42
+ "docs/dev/status.md"
43
+ "docs/dev/CONTRIBUTING.md"
44
+ "docs/dev/EXTENSION_SOP.md"
45
+ ".pi/extensions/startup-intake-router.ts"
46
+ ".pi/prompts/init-project-docs.md"
47
+ "scripts/sync-to-live.sh"
48
+ "scripts/install-hooks.sh"
49
+ "scripts/init-project-docs.sh"
50
+ "scripts/docs-list.sh"
51
+ "scripts/validate-structure.sh"
52
+ ".githooks/pre-commit"
53
+ "prompts/implementation-planning.md"
54
+ "prompts/security-review.md"
55
+ "prompts/init-project-docs.md"
56
+ )
57
+
58
+ required_dirs=(
59
+ ".github"
60
+ ".github/workflows"
61
+ ".github/ISSUE_TEMPLATE"
62
+ "docs/org"
63
+ "docs/dev/plans"
64
+ "examples"
65
+ "external"
66
+ "ontology"
67
+ "policy"
68
+ "scripts"
69
+ "src"
70
+ "tests"
71
+ ".pi"
72
+ ".pi/extensions"
73
+ ".pi/prompts"
74
+ ".githooks"
75
+ "prompts"
76
+ )
77
+
78
+ required_executables=(
79
+ "scripts/sync-to-live.sh"
80
+ "scripts/install-hooks.sh"
81
+ "scripts/init-project-docs.sh"
82
+ "scripts/docs-list.sh"
83
+ "scripts/validate-structure.sh"
84
+ ".githooks/pre-commit"
85
+ )
86
+
87
+ errors=0
88
+
89
+ for required_file in "${required_files[@]}"; do
90
+ if [[ ! -f "$required_file" ]]; then
91
+ echo "Missing required file: $required_file" >&2
92
+ ((errors+=1))
93
+ fi
94
+ done
95
+
96
+ for required_dir in "${required_dirs[@]}"; do
97
+ if [[ ! -d "$required_dir" ]]; then
98
+ echo "Missing required directory: $required_dir" >&2
99
+ ((errors+=1))
100
+ fi
101
+ done
102
+
103
+ for executable in "${required_executables[@]}"; do
104
+ if [[ ! -x "$executable" ]]; then
105
+ echo "Expected executable bit on: $executable" >&2
106
+ ((errors+=1))
107
+ fi
108
+ done
109
+
110
+ plan_count=$(find "docs/dev/plans" -maxdepth 1 -type f -name "*.md" | wc -l | tr -d ' ')
111
+ if [[ "$plan_count" -lt 1 ]]; then
112
+ echo "docs/dev/plans must contain at least one markdown plan file" >&2
113
+ ((errors+=1))
114
+ fi
115
+
116
+ for copier_key in "_src_path:" "repo_name:" "command_name:"; do
117
+ if ! grep -q "^${copier_key}" ".copier-answers.yml"; then
118
+ echo "Missing copier answer key in .copier-answers.yml: ${copier_key}" >&2
119
+ ((errors+=1))
120
+ fi
121
+ done
122
+
123
+ placeholder_pattern='\{username\}|\{repo\}|\{discordInvite\}|\{@twitter\}'
124
+ placeholder_hits="$(grep -R -nE "$placeholder_pattern" .github || true)"
125
+ if [[ -n "$placeholder_hits" ]]; then
126
+ echo "Unresolved placeholders found under .github:" >&2
127
+ echo "$placeholder_hits" >&2
128
+ ((errors+=1))
129
+ fi
130
+
131
+ vouch_ref="0e11a71bba23218a284d3ecca162e75a110fd7e3"
132
+ if ! grep -q "mitchellh/vouch/action/check-pr@${vouch_ref}" ".github/workflows/vouch-check-pr.yml"; then
133
+ echo "vouch-check-pr workflow must pin mitchellh/vouch/action/check-pr to ${vouch_ref}" >&2
134
+ ((errors+=1))
135
+ fi
136
+
137
+ if ! grep -q "mitchellh/vouch/action/manage-by-issue@${vouch_ref}" ".github/workflows/vouch-manage.yml"; then
138
+ echo "vouch-manage workflow must pin mitchellh/vouch/action/manage-by-issue to ${vouch_ref}" >&2
139
+ ((errors+=1))
140
+ fi
141
+
142
+ if grep -n "@main" .github/workflows/vouch-*.yml >/dev/null 2>&1; then
143
+ echo "vouch workflows must not use @main refs" >&2
144
+ ((errors+=1))
145
+ fi
146
+
147
+ if ! grep -q "pull_request_target" ".github/workflows/vouch-check-pr.yml"; then
148
+ echo "vouch-check-pr workflow must trigger on pull_request_target" >&2
149
+ ((errors+=1))
150
+ fi
151
+
152
+ if ! grep -q "require-vouch" ".github/workflows/vouch-check-pr.yml"; then
153
+ echo "vouch-check-pr workflow must set require-vouch" >&2
154
+ ((errors+=1))
155
+ fi
156
+
157
+ if ! grep -q "auto-close" ".github/workflows/vouch-check-pr.yml"; then
158
+ echo "vouch-check-pr workflow must set auto-close" >&2
159
+ ((errors+=1))
160
+ fi
161
+
162
+ if ! grep -q "issue_comment" ".github/workflows/vouch-manage.yml"; then
163
+ echo "vouch-manage workflow must trigger on issue_comment" >&2
164
+ ((errors+=1))
165
+ fi
166
+
167
+ if ! grep -q "concurrency:" ".github/workflows/vouch-manage.yml" || ! grep -q "group: vouch-manage" ".github/workflows/vouch-manage.yml"; then
168
+ echo "vouch-manage workflow must define serialized concurrency" >&2
169
+ ((errors+=1))
170
+ fi
171
+
172
+ if ! grep -q "vouched-file: .github/VOUCHED.td" ".github/workflows/vouch-manage.yml"; then
173
+ echo "vouch-manage workflow must target .github/VOUCHED.td" >&2
174
+ ((errors+=1))
175
+ fi
176
+
177
+ if grep -q "@your-github-handle" ".github/CODEOWNERS"; then
178
+ echo ".github/CODEOWNERS must not keep @your-github-handle placeholder" >&2
179
+ ((errors+=1))
180
+ fi
181
+
182
+ if ! grep -Eq "^github:[A-Za-z0-9][A-Za-z0-9-]*" ".github/VOUCHED.td"; then
183
+ echo ".github/VOUCHED.td must include at least one github maintainer entry" >&2
184
+ ((errors+=1))
185
+ fi
186
+
187
+ if command -v node >/dev/null 2>&1; then
188
+ if ! node - <<'NODE'
189
+ const fs = require("node:fs");
190
+
191
+ let failed = false;
192
+ const fail = (msg) => {
193
+ console.error(msg);
194
+ failed = true;
195
+ };
196
+
197
+ try {
198
+ const qPath = "docs/org/project-docs-intake.questions.json";
199
+ const q = JSON.parse(fs.readFileSync(qPath, "utf8"));
200
+ if (!q.title || !Array.isArray(q.questions) || q.questions.length === 0) {
201
+ fail(`Invalid interview questions file: ${qPath}`);
202
+ }
203
+ } catch (error) {
204
+ fail(`Failed to parse interview questions file: ${error.message}`);
205
+ }
206
+
207
+ try {
208
+ const p = JSON.parse(fs.readFileSync("package.json", "utf8"));
209
+ if (!Array.isArray(p.keywords) || !p.keywords.includes("pi-package")) {
210
+ fail("package.json missing keywords entry: pi-package");
211
+ }
212
+
213
+ const ext = p.pi?.extensions;
214
+ if (!Array.isArray(ext) || ext.length < 1) {
215
+ fail("package.json missing pi.extensions array");
216
+ } else {
217
+ for (const entry of ext) {
218
+ const normalized = entry.replace(/^\.\//, "");
219
+ if (!fs.existsSync(normalized)) {
220
+ fail(`pi.extensions entry does not exist: ${entry}`);
221
+ }
222
+ }
223
+ }
224
+
225
+ const prompts = p.pi?.prompts;
226
+ if (!Array.isArray(prompts) || prompts.length < 1) {
227
+ fail("package.json missing pi.prompts array");
228
+ } else {
229
+ for (const entry of prompts) {
230
+ const normalized = entry.replace(/\/$/, "").replace(/^\.\//, "");
231
+ if (!fs.existsSync(normalized)) {
232
+ fail(`pi.prompts entry does not exist: ${entry}`);
233
+ }
234
+ }
235
+ }
236
+
237
+ const checkScript = p.scripts?.check;
238
+ if (checkScript !== "bash ./scripts/validate-structure.sh") {
239
+ fail("package.json scripts.check must be 'bash ./scripts/validate-structure.sh'");
240
+ }
241
+
242
+ const testScript = p.scripts?.test;
243
+ if (testScript !== "bash ./scripts/validate-structure.sh") {
244
+ fail("package.json scripts.test must be 'bash ./scripts/validate-structure.sh'");
245
+ }
246
+
247
+ const docsListScript = p.scripts?.["docs:list"];
248
+ if (docsListScript !== "bash ./scripts/docs-list.sh") {
249
+ fail("package.json scripts.docs:list must be 'bash ./scripts/docs-list.sh'");
250
+ }
251
+
252
+ const docsListWorkspaceScript = p.scripts?.["docs:list:workspace"];
253
+ if (docsListWorkspaceScript !== "bash ./scripts/docs-list.sh --workspace --discover") {
254
+ fail("package.json scripts.docs:list:workspace must be 'bash ./scripts/docs-list.sh --workspace --discover'");
255
+ }
256
+
257
+ const docsListJsonScript = p.scripts?.["docs:list:json"];
258
+ if (docsListJsonScript !== "bash ./scripts/docs-list.sh --json") {
259
+ fail("package.json scripts.docs:list:json must be 'bash ./scripts/docs-list.sh --json'");
260
+ }
261
+
262
+ const rpConfig = JSON.parse(fs.readFileSync(".release-please-config.json", "utf8"));
263
+ if (rpConfig["include-v-in-tag"] !== true) {
264
+ fail(".release-please-config.json must set include-v-in-tag=true");
265
+ }
266
+ if (!rpConfig.packages || !rpConfig.packages["."]) {
267
+ fail(".release-please-config.json must include packages['.']");
268
+ }
269
+
270
+ const rpManifest = JSON.parse(fs.readFileSync(".release-please-manifest.json", "utf8"));
271
+ if (!rpManifest["."]) {
272
+ fail(".release-please-manifest.json must include '.' version entry");
273
+ }
274
+ const versionPattern = /^\d+\.\d+\.\d+([-.][0-9A-Za-z.]+)?$/;
275
+ if (!versionPattern.test(rpManifest["."])) {
276
+ fail(".release-please-manifest.json '.' entry must match X.Y.Z");
277
+ }
278
+ if (rpManifest["."] !== p.version) {
279
+ fail(".release-please-manifest.json '.' entry must match package.json version");
280
+ }
281
+ } catch (error) {
282
+ fail(`Failed to validate package/release metadata: ${error.message}`);
283
+ }
284
+
285
+ process.exit(failed ? 1 : 0);
286
+ NODE
287
+ then
288
+ ((errors+=1))
289
+ fi
290
+ fi
291
+
292
+ while IFS= read -r -d '' markdown_file; do
293
+ if [[ "$(head -n 1 "$markdown_file")" != "---" ]]; then
294
+ echo "Missing YAML frontmatter start in: $markdown_file" >&2
295
+ ((errors+=1))
296
+ continue
297
+ fi
298
+
299
+ if ! grep -q "^system4d:" "$markdown_file"; then
300
+ echo "Missing system4d section in: $markdown_file" >&2
301
+ ((errors+=1))
302
+ continue
303
+ fi
304
+
305
+ for key in container compass engine fog; do
306
+ if ! grep -q "^ $key:" "$markdown_file"; then
307
+ echo "Missing system4d.$key in: $markdown_file" >&2
308
+ ((errors+=1))
309
+ fi
310
+ done
311
+
312
+ if [[ "$markdown_file" == "./prompts/"* || "$markdown_file" == "./.pi/prompts/"* ]]; then
313
+ if ! grep -q "^description:" "$markdown_file"; then
314
+ echo "Prompt template missing frontmatter description: $markdown_file" >&2
315
+ ((errors+=1))
316
+ fi
317
+ fi
318
+ done < <(find . -type f -name "*.md" ! -path "./.git/*" ! -path "./node_modules/*" -print0)
319
+
320
+ if [[ "$errors" -gt 0 ]]; then
321
+ echo "Structure validation failed with $errors issue(s)." >&2
322
+ exit 1
323
+ fi
324
+
325
+ echo "Structure validation passed."
package/src/.gitkeep ADDED
File without changes
package/tests/.gitkeep ADDED
File without changes