@piagent/platform 1.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.
Files changed (159) hide show
  1. package/.gitattributes +7 -0
  2. package/AGENTS.md +31 -0
  3. package/CHANGELOG.md +296 -0
  4. package/CODE_OF_CONDUCT.md +36 -0
  5. package/CONTRIBUTING.md +66 -0
  6. package/LICENSE +21 -0
  7. package/README.md +588 -0
  8. package/SECURITY.md +38 -0
  9. package/adapters/backend-api/profile.json +88 -0
  10. package/adapters/be-readonly-fe/profile.json +120 -0
  11. package/adapters/data/profile.json +88 -0
  12. package/adapters/devops/profile.json +92 -0
  13. package/adapters/docs/profile.json +74 -0
  14. package/adapters/fullstack/profile.json +94 -0
  15. package/adapters/generic/profile.json +95 -0
  16. package/adapters/mobile/profile.json +87 -0
  17. package/adapters/node-typescript/profile.json +76 -0
  18. package/adapters/python/profile.json +75 -0
  19. package/adapters/web-frontend/profile.json +101 -0
  20. package/catalog/capabilities.json +206 -0
  21. package/docs/architecture.md +64 -0
  22. package/docs/auto-delegation-policy.md +192 -0
  23. package/docs/capability-packs.md +234 -0
  24. package/docs/command-reference-vietnamese.md +484 -0
  25. package/docs/context-window-policy.md +85 -0
  26. package/docs/distribution-standard.md +229 -0
  27. package/docs/herdr-workflow.md +58 -0
  28. package/docs/mcp-and-tools.md +316 -0
  29. package/docs/memory-policy.md +164 -0
  30. package/docs/model-options.md +165 -0
  31. package/docs/oauth-providers.md +53 -0
  32. package/docs/operator-manual-vietnamese.md +1080 -0
  33. package/docs/package-architecture-notes.md +101 -0
  34. package/docs/project-adapters.md +174 -0
  35. package/docs/project-onboarding.md +135 -0
  36. package/docs/publishing-for-teams.md +64 -0
  37. package/docs/quality-benchmark.md +74 -0
  38. package/docs/quickstart-vietnamese.md +350 -0
  39. package/docs/release-install-policy.md +223 -0
  40. package/docs/runtime-harness-standard.md +118 -0
  41. package/docs/runtime-policy-design.md +132 -0
  42. package/docs/runtime-quality-baseline.md +257 -0
  43. package/docs/security-redaction-benchmark.md +56 -0
  44. package/docs/security-threat-model.md +66 -0
  45. package/docs/subagent-orchestration-capabilities.md +104 -0
  46. package/docs/subagents-and-multiagent.md +346 -0
  47. package/docs/task-implementation-contract.md +200 -0
  48. package/docs/team-onboarding.md +333 -0
  49. package/docs/usage-observability.md +130 -0
  50. package/docs/vercel-docs-site.md +115 -0
  51. package/docs/workflow-recipes.md +125 -0
  52. package/docs-site/assets/piagent-logo.svg +35 -0
  53. package/docs-site/favicon.svg +18 -0
  54. package/docs-site/index.html +1613 -0
  55. package/docs-site/vercel.json +24 -0
  56. package/evals/fixtures/action-proposal.invalid.json +34 -0
  57. package/evals/fixtures/action-proposal.valid.json +34 -0
  58. package/evals/fixtures/capability-pack.invalid.json +123 -0
  59. package/evals/fixtures/capability-pack.valid.json +123 -0
  60. package/evals/fixtures/capability-recipe.invalid.json +81 -0
  61. package/evals/fixtures/capability-recipe.valid.json +82 -0
  62. package/evals/fixtures/context-index.invalid.json +25 -0
  63. package/evals/fixtures/context-index.valid.json +25 -0
  64. package/evals/fixtures/eval-scenario.invalid.json +40 -0
  65. package/evals/fixtures/eval-scenario.valid.json +45 -0
  66. package/evals/fixtures/project-profile.invalid.json +88 -0
  67. package/evals/fixtures/project-profile.valid.json +95 -0
  68. package/evals/fixtures/task-contract.invalid.json +73 -0
  69. package/evals/fixtures/task-contract.valid.json +74 -0
  70. package/evals/golden/enforcement-decisions.json +329 -0
  71. package/evals/scenarios/capability-resolution.json +45 -0
  72. package/package.json +132 -0
  73. package/packages/piagent-core/README.md +147 -0
  74. package/packages/piagent-core/capabilities/capability-core.js +1023 -0
  75. package/packages/piagent-core/capabilities/capability-sources.js +265 -0
  76. package/packages/piagent-core/extensions/guard-io.js +45 -0
  77. package/packages/piagent-core/extensions/guard-shell-analysis.ts +558 -0
  78. package/packages/piagent-core/extensions/guard-types.ts +406 -0
  79. package/packages/piagent-core/extensions/piagent-guard.ts +5760 -0
  80. package/packages/piagent-core/extensions/policy-core.js +1070 -0
  81. package/packages/piagent-core/extensions/redaction-core.js +4 -0
  82. package/packages/piagent-core/extensions/runtime-evidence.js +215 -0
  83. package/packages/piagent-core/package.json +46 -0
  84. package/packages/piagent-core/policies/base-policy.json +249 -0
  85. package/packages/piagent-core/prompts/be-to-fe.md +82 -0
  86. package/packages/piagent-core/prompts/commit.md +27 -0
  87. package/packages/piagent-core/prompts/discuss.md +35 -0
  88. package/packages/piagent-core/prompts/memory-policy.md +52 -0
  89. package/packages/piagent-core/prompts/model-options.md +68 -0
  90. package/packages/piagent-core/prompts/onboard-project.md +147 -0
  91. package/packages/piagent-core/prompts/piagent-commands.md +68 -0
  92. package/packages/piagent-core/prompts/plan.md +35 -0
  93. package/packages/piagent-core/prompts/platform-improve.md +69 -0
  94. package/packages/piagent-core/prompts/pr.md +26 -0
  95. package/packages/piagent-core/prompts/review.md +38 -0
  96. package/packages/piagent-core/prompts/scout.md +50 -0
  97. package/packages/piagent-core/prompts/task.md +69 -0
  98. package/packages/piagent-core/security/sensitive-data.js +187 -0
  99. package/packages/piagent-core/skills/piagent-ops/SKILL.md +44 -0
  100. package/packages/piagent-core/skills/piagent-source-cache/SKILL.md +59 -0
  101. package/packages/piagent-core/skills/piagent-source-cache/checkout-source-repo.sh +144 -0
  102. package/packages/piagent-core/subagents/piagent-oracle.md +29 -0
  103. package/packages/piagent-core/subagents/piagent-planner.md +46 -0
  104. package/packages/piagent-core/subagents/piagent-reviewer.md +48 -0
  105. package/packages/piagent-core/subagents/piagent-scout.md +37 -0
  106. package/packages/piagent-core/subagents/piagent-worker.md +44 -0
  107. package/packs/engineering-base/pack.json +123 -0
  108. package/packs/engineering-base/recipes/bounded-change.json +82 -0
  109. package/packs/web-delivery/pack.json +72 -0
  110. package/packs/web-delivery/recipes/verified-web-change.json +57 -0
  111. package/schemas/action-proposal.schema.json +90 -0
  112. package/schemas/capability-pack.schema.json +190 -0
  113. package/schemas/capability-recipe.schema.json +72 -0
  114. package/schemas/context-index.schema.json +258 -0
  115. package/schemas/eval-scenario.schema.json +67 -0
  116. package/schemas/project-profile.schema.json +555 -0
  117. package/schemas/task-contract.schema.json +314 -0
  118. package/scripts/audit-runtime-host.sh +78 -0
  119. package/scripts/bootstrap-local.sh +25 -0
  120. package/scripts/capability-catalog.mjs +290 -0
  121. package/scripts/check-runtime-advisories.mjs +114 -0
  122. package/scripts/configure-mcp.sh +301 -0
  123. package/scripts/configure-model-scope.sh +143 -0
  124. package/scripts/configure-subagents.sh +241 -0
  125. package/scripts/import-agent-instructions.mjs +199 -0
  126. package/scripts/init-project.sh +369 -0
  127. package/scripts/install-global.sh +532 -0
  128. package/scripts/link-project.sh +25 -0
  129. package/scripts/migrate-project-state.mjs +176 -0
  130. package/scripts/pi-auto.sh +79 -0
  131. package/scripts/pi-model-catalog.sh +154 -0
  132. package/scripts/pi-session-stats.sh +135 -0
  133. package/scripts/piagent-cli.mjs +53 -0
  134. package/scripts/profile-doctor.sh +238 -0
  135. package/scripts/quality-benchmark.sh +185 -0
  136. package/scripts/redaction-benchmark.mjs +227 -0
  137. package/scripts/runtime-policy-smoke.sh +48 -0
  138. package/scripts/setup.sh +425 -0
  139. package/scripts/team-doctor.sh +461 -0
  140. package/scripts/verify-local.sh +540 -0
  141. package/scripts/verify-release-identity.mjs +85 -0
  142. package/scripts/verify-vercel-link.mjs +64 -0
  143. package/templates/global/mcp.json +9 -0
  144. package/templates/global/settings.json +37 -0
  145. package/templates/project/.mcp.json +9 -0
  146. package/templates/project/.pi/context-index.json +25 -0
  147. package/templates/project/.pi/gitignore.template +22 -0
  148. package/templates/project/.pi/mcp.json +9 -0
  149. package/templates/project/.pi/memory/MEMORY.md +11 -0
  150. package/templates/project/.pi/memory/memory_summary.md +9 -0
  151. package/templates/project/.pi/piagent-profile.json +107 -0
  152. package/templates/project/.pi/project-context.md +30 -0
  153. package/templates/project/.pi/settings.json +17 -0
  154. package/templates/project/.pi/task-contract.template.json +74 -0
  155. package/templates/project/.pi/tech-context/README.md +7 -0
  156. package/templates/project/.pi/tech-stack.json +10 -0
  157. package/templates/project/AGENTS.md +34 -0
  158. package/templates/project/REVIEW_GUIDELINES.md +23 -0
  159. package/types/pi-runtime-shims.d.ts +13 -0
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ // Imports instruction files written for other agents into AGENTS.md.
3
+ //
4
+ // AGENTS.md is the format this platform reads, and the one the wider ecosystem
5
+ // converged on, so the flow is deliberately one-way: legacy formats are folded
6
+ // in and then stop being a second source of truth.
7
+ //
8
+ // Imported text is DATA, not instruction. It is quoted into a clearly labelled
9
+ // section and never interpreted here: a repository's instruction file is
10
+ // attacker-controlled in the general case, and anything that could change
11
+ // protected paths, permission profile, or verify commands is reported for a
12
+ // human to act on rather than applied.
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+
16
+ // Ordered: earlier sources win a conflict. AGENTS.md itself always outranks
17
+ // every import, which is what makes repeated runs safe.
18
+ const SOURCES = [
19
+ { id: "claude-md", label: "CLAUDE.md", kind: "file", rel: "CLAUDE.md" },
20
+ { id: "claude-rules", label: ".claude/rules", kind: "dir", rel: ".claude/rules", ext: [".md"] },
21
+ { id: "cursor-rules", label: ".cursor/rules", kind: "dir", rel: ".cursor/rules", ext: [".md", ".mdc"] },
22
+ { id: "copilot", label: ".github/copilot-instructions.md", kind: "file", rel: ".github/copilot-instructions.md" }
23
+ ];
24
+
25
+ const IMPORT_HEADING = "## Imported agent instructions";
26
+
27
+ // Directives that would change what the guard enforces. Matching text is still
28
+ // imported verbatim, but it is surfaced so a human decides rather than having
29
+ // the profile quietly rewritten by a file from an untrusted repository.
30
+ const SENSITIVE_PATTERNS = [
31
+ { id: "protected-paths", pattern: /\bprotected\s*paths?\b/i },
32
+ { id: "permission-profile", pattern: /\b(trusted-full-access|full[- ]access|permission\s*profile)\b/i },
33
+ { id: "verify-commands", pattern: /\bverify\s*commands?\b/i },
34
+ { id: "policy-override", pattern: /\b(ignore|disregard|override|bypass)\b[^.\n]{0,40}\b(rule|policy|instruction|guard|restriction)/i },
35
+ { id: "secret-handling", pattern: /\b(\.env|auth\.json|api[_ -]?key|secret|credential)/i }
36
+ ];
37
+
38
+ function usage() {
39
+ console.log(`Usage:
40
+ piagent-import-instructions [project-path] [--apply]
41
+
42
+ Folds instruction files written for other agents into AGENTS.md.
43
+
44
+ (default) Dry run. Prints the plan, conflicts, and flagged directives.
45
+ --apply Append the imported section to AGENTS.md.
46
+
47
+ Sources, in precedence order (earlier wins):
48
+ ${SOURCES.map((source, index) => ` ${index + 1}. ${source.label}`).join("\n")}
49
+
50
+ AGENTS.md always outranks every import, so re-running never overwrites
51
+ content you have already reviewed.`);
52
+ }
53
+
54
+ const args = process.argv.slice(2);
55
+ if (args.includes("-h") || args.includes("--help")) {
56
+ usage();
57
+ process.exit(0);
58
+ }
59
+ const apply = args.includes("--apply");
60
+ const positional = args.filter((arg) => !arg.startsWith("--"));
61
+ if (positional.length > 1) {
62
+ console.error(`Expected at most one project path, received ${positional.length}.`);
63
+ process.exit(2);
64
+ }
65
+ const projectPath = path.resolve(positional[0] ?? process.cwd());
66
+ if (!fs.existsSync(projectPath)) {
67
+ console.error(`Project path does not exist: ${projectPath}`);
68
+ process.exit(2);
69
+ }
70
+
71
+ function readIfRegularFile(absolute) {
72
+ const stat = fs.lstatSync(absolute, { throwIfNoEntry: false });
73
+ if (!stat || !stat.isFile()) return undefined;
74
+ return fs.readFileSync(absolute, "utf8");
75
+ }
76
+
77
+ function collect(source) {
78
+ const absolute = path.join(projectPath, source.rel);
79
+ if (!fs.existsSync(absolute)) return [];
80
+ if (source.kind === "file") {
81
+ const text = readIfRegularFile(absolute);
82
+ return text === undefined ? [] : [{ origin: source.rel, sourceId: source.id, text }];
83
+ }
84
+ const stat = fs.lstatSync(absolute);
85
+ if (!stat.isDirectory()) return [];
86
+ return fs.readdirSync(absolute)
87
+ .filter((name) => source.ext.some((ext) => name.endsWith(ext)))
88
+ .sort()
89
+ .map((name) => {
90
+ const text = readIfRegularFile(path.join(absolute, name));
91
+ return text === undefined ? undefined : { origin: path.posix.join(source.rel, name), sourceId: source.id, text };
92
+ })
93
+ .filter(Boolean);
94
+ }
95
+
96
+ // A heading is the unit of conflict: two sources describing "Build commands"
97
+ // differently is the case a human needs to see.
98
+ function headings(text) {
99
+ return text
100
+ .split("\n")
101
+ .filter((line) => /^#{1,6}\s+\S/.test(line))
102
+ .map((line) => line.replace(/^#+\s+/, "").trim().toLowerCase());
103
+ }
104
+
105
+ const documents = SOURCES.flatMap(collect);
106
+ const agentsPath = path.join(projectPath, "AGENTS.md");
107
+ const agentsText = fs.existsSync(agentsPath) ? fs.readFileSync(agentsPath, "utf8") : "";
108
+ const alreadyImported = agentsText.includes(IMPORT_HEADING);
109
+
110
+ const flagged = [];
111
+ for (const document of documents) {
112
+ for (const line of document.text.split("\n")) {
113
+ for (const { id, pattern } of SENSITIVE_PATTERNS) {
114
+ if (pattern.test(line)) flagged.push({ origin: document.origin, rule: id, line: line.trim().slice(0, 200) });
115
+ }
116
+ }
117
+ }
118
+
119
+ const seen = new Map();
120
+ const conflicts = [];
121
+ const agentsHeadings = new Set(headings(agentsText));
122
+ for (const document of documents) {
123
+ for (const heading of headings(document.text)) {
124
+ if (agentsHeadings.has(heading)) {
125
+ conflicts.push({ heading, origin: document.origin, resolution: "AGENTS.md wins" });
126
+ continue;
127
+ }
128
+ if (seen.has(heading)) {
129
+ conflicts.push({ heading, origin: document.origin, resolution: `${seen.get(heading)} wins` });
130
+ continue;
131
+ }
132
+ seen.set(heading, document.origin);
133
+ }
134
+ }
135
+
136
+ function renderImportSection() {
137
+ const lines = [
138
+ "",
139
+ IMPORT_HEADING,
140
+ "",
141
+ "Folded in from instruction files written for other agents. Treated as",
142
+ "reference material: rules here do not change protected paths, permission",
143
+ "profile, or verify commands unless a maintainer moves them into the",
144
+ "sections above.",
145
+ ""
146
+ ];
147
+ for (const document of documents) {
148
+ lines.push(`### From \`${document.origin}\``, "");
149
+ lines.push(document.text.trimEnd(), "");
150
+ }
151
+ return lines.join("\n");
152
+ }
153
+
154
+ const report = {
155
+ ok: true,
156
+ projectPath,
157
+ sourcesFound: documents.map((document) => document.origin),
158
+ conflicts,
159
+ flaggedDirectives: flagged,
160
+ alreadyImported
161
+ };
162
+
163
+ if (documents.length === 0) {
164
+ console.log(JSON.stringify({ ...report, imported: false, reason: "no instruction files from other agents found" }, null, 2));
165
+ process.exit(0);
166
+ }
167
+
168
+ if (alreadyImported) {
169
+ console.log(JSON.stringify({
170
+ ...report,
171
+ imported: false,
172
+ reason: "AGENTS.md already contains an imported section; remove it first to re-import"
173
+ }, null, 2));
174
+ process.exit(0);
175
+ }
176
+
177
+ if (!apply) {
178
+ console.log(JSON.stringify({
179
+ ...report,
180
+ dryRun: true,
181
+ wouldAppendTo: "AGENTS.md",
182
+ wouldAppendBytes: Buffer.byteLength(renderImportSection()),
183
+ next: flagged.length > 0
184
+ ? "review flaggedDirectives, then re-run with --apply"
185
+ : "re-run with --apply"
186
+ }, null, 2));
187
+ process.exit(0);
188
+ }
189
+
190
+ fs.writeFileSync(agentsPath, `${agentsText.trimEnd()}\n${renderImportSection()}`);
191
+
192
+ console.log(JSON.stringify({
193
+ ...report,
194
+ imported: true,
195
+ appendedTo: "AGENTS.md",
196
+ next: flagged.length > 0
197
+ ? "review flaggedDirectives; nothing in them has been applied to the profile"
198
+ : "review AGENTS.md and delete the now-redundant source files when satisfied"
199
+ }, null, 2));
@@ -0,0 +1,369 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'USAGE'
6
+ Usage:
7
+ scripts/init-project.sh <project-path> [options]
8
+
9
+ Options:
10
+ --profile <name-or-json> auto | generic | web-frontend | backend-api | be-readonly-fe | fullstack | node-typescript | python | data | devops | mobile | docs | path/to/profile.json
11
+ --package-source <source> Pi package source committed into .pi/settings.json
12
+ --force-profile Replace existing .pi/piagent-profile.json
13
+ --force-settings Replace existing .pi/settings.json
14
+ --skip-agents Do not create AGENTS.md
15
+ --skip-review-guidelines Do not create REVIEW_GUIDELINES.md
16
+ -h, --help
17
+
18
+ Package source examples:
19
+ # Exact sources are required here because init writes .pi/settings.json and a capability lock:
20
+ git:github.com/Vt-mmm/piagent@vX.Y.Z
21
+ https://github.com/Vt-mmm/piagent/archive/refs/tags/vX.Y.Z.tar.gz
22
+ npm:@your-scope/platform@x.y.z
23
+
24
+ Default package source:
25
+ 1. --package-source
26
+ 2. PIAGENT_PACKAGE_SOURCE
27
+ 3. local platform path (warn; do not commit for team)
28
+ USAGE
29
+ }
30
+
31
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
32
+ usage
33
+ exit 0
34
+ fi
35
+
36
+ if [[ $# -lt 1 ]]; then
37
+ usage >&2
38
+ exit 2
39
+ fi
40
+
41
+ PROJECT_PATH="$1"
42
+ shift
43
+
44
+ PLATFORM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
45
+ PROFILE_INPUT="auto"
46
+ PACKAGE_SOURCE="${PIAGENT_PACKAGE_SOURCE:-}"
47
+ FORCE_PROFILE=false
48
+ FORCE_SETTINGS=false
49
+ SKIP_AGENTS=false
50
+ SKIP_REVIEW=false
51
+
52
+ while [[ $# -gt 0 ]]; do
53
+ case "$1" in
54
+ --profile)
55
+ if [[ $# -lt 2 ]]; then
56
+ echo "Missing value for --profile" >&2
57
+ exit 2
58
+ fi
59
+ PROFILE_INPUT="$2"
60
+ shift 2
61
+ ;;
62
+ --package-source)
63
+ if [[ $# -lt 2 ]]; then
64
+ echo "Missing value for --package-source" >&2
65
+ exit 2
66
+ fi
67
+ PACKAGE_SOURCE="$2"
68
+ shift 2
69
+ ;;
70
+ --force-profile)
71
+ FORCE_PROFILE=true
72
+ shift
73
+ ;;
74
+ --force-settings)
75
+ FORCE_SETTINGS=true
76
+ shift
77
+ ;;
78
+ --skip-agents)
79
+ SKIP_AGENTS=true
80
+ shift
81
+ ;;
82
+ --skip-review-guidelines)
83
+ SKIP_REVIEW=true
84
+ shift
85
+ ;;
86
+ -h|--help)
87
+ usage
88
+ exit 0
89
+ ;;
90
+ *)
91
+ echo "Unknown option: $1" >&2
92
+ usage >&2
93
+ exit 2
94
+ ;;
95
+ esac
96
+ done
97
+
98
+ if [[ ! -d "$PROJECT_PATH" ]]; then
99
+ echo "FAIL: project path does not exist: $PROJECT_PATH" >&2
100
+ exit 1
101
+ fi
102
+
103
+ PROJECT_PATH="$(cd "$PROJECT_PATH" && pwd)"
104
+
105
+ package_has_any() {
106
+ local project="$1"
107
+ local pattern="$2"
108
+ [[ -f "$project/package.json" ]] && grep -Eiq "$pattern" "$project/package.json"
109
+ }
110
+
111
+ detect_profile() {
112
+ local project="$1"
113
+ local has_package=false
114
+ local frontend=false
115
+ local backend=false
116
+ local data=false
117
+ local mobile=false
118
+ local devops=false
119
+ local infra=false
120
+ local docs=false
121
+
122
+ [[ -f "$project/package.json" ]] && has_package=true
123
+
124
+ if [[ -f "$project/pubspec.yaml" ]] || { [[ -d "$project/android" ]] && [[ -d "$project/ios" ]]; }; then
125
+ mobile=true
126
+ fi
127
+
128
+ if [[ -f "$project/dbt_project.yml" ]] || [[ -f "$project/dvc.yaml" ]] || [[ -d "$project/notebooks" ]] || [[ -d "$project/data" ]]; then
129
+ data=true
130
+ fi
131
+
132
+ if [[ -f "$project/Dockerfile" ]] || [[ -f "$project/docker-compose.yml" ]] || [[ -f "$project/compose.yml" ]] || [[ -f "$project/compose.yaml" ]] || [[ -d "$project/terraform" ]] || [[ -d "$project/infra" ]] || [[ -d "$project/k8s" ]] || [[ -d "$project/helm" ]]; then
133
+ infra=true
134
+ devops=true
135
+ elif [[ -d "$project/.github/workflows" ]]; then
136
+ devops=true
137
+ fi
138
+
139
+ if [[ -d "$project/docs" ]] || [[ -f "$project/mkdocs.yml" ]] || [[ -f "$project/mint.json" ]] || [[ -f "$project/docusaurus.config.js" ]]; then
140
+ docs=true
141
+ fi
142
+
143
+ if [[ "$has_package" == true ]]; then
144
+ if [[ -d "$project/frontend" ]] || [[ -d "$project/apps/web" ]] || [[ -d "$project/apps/frontend" ]]; then
145
+ frontend=true
146
+ fi
147
+
148
+ if [[ -d "$project/backend" ]] || [[ -d "$project/apps/api" ]] || [[ -d "$project/apps/server" ]]; then
149
+ backend=true
150
+ fi
151
+
152
+ if package_has_any "$project" '"(next|react|vite|vue|svelte|astro|@angular/core|remix)"' \
153
+ || [[ -f "$project/next.config.js" ]] || [[ -f "$project/next.config.mjs" ]] || [[ -f "$project/next.config.ts" ]] \
154
+ || [[ -f "$project/vite.config.js" ]] || [[ -f "$project/vite.config.ts" ]] \
155
+ || [[ -d "$project/src/app" ]] || [[ -d "$project/pages" ]] || [[ -d "$project/public" ]]; then
156
+ frontend=true
157
+ fi
158
+
159
+ if package_has_any "$project" '"(@nestjs|express|fastify|hono|koa|apollo-server|graphql-yoga|prisma|typeorm|sequelize|drizzle-orm)"' \
160
+ || [[ -f "$project/nest-cli.json" ]] || [[ -d "$project/prisma" ]] || [[ -d "$project/src/server" ]] || [[ -d "$project/src/api" ]]; then
161
+ backend=true
162
+ fi
163
+ fi
164
+
165
+ if [[ -f "$project/pom.xml" ]] || [[ -f "$project/build.gradle" ]] || [[ -f "$project/build.gradle.kts" ]] || [[ -d "$project/src/main/java" ]] || [[ -d "$project/src/main/kotlin" ]]; then
166
+ backend=true
167
+ fi
168
+
169
+ if [[ -f "$project/pyproject.toml" ]] && grep -Eiq "(fastapi|flask|django|litestar|starlite)" "$project/pyproject.toml"; then
170
+ backend=true
171
+ fi
172
+
173
+ if [[ "$mobile" == true ]]; then
174
+ printf '%s\n' "mobile"
175
+ elif [[ "$frontend" == true && "$backend" == true ]]; then
176
+ printf '%s\n' "fullstack"
177
+ elif [[ "$frontend" == true ]]; then
178
+ printf '%s\n' "web-frontend"
179
+ elif [[ "$backend" == true ]]; then
180
+ printf '%s\n' "backend-api"
181
+ elif [[ "$data" == true ]]; then
182
+ printf '%s\n' "data"
183
+ elif [[ -f "$project/pyproject.toml" ]]; then
184
+ printf '%s\n' "python"
185
+ elif [[ "$has_package" == true && -f "$project/tsconfig.json" ]]; then
186
+ printf '%s\n' "node-typescript"
187
+ elif [[ "$infra" == true ]]; then
188
+ printf '%s\n' "devops"
189
+ elif [[ "$docs" == true ]]; then
190
+ printf '%s\n' "docs"
191
+ elif [[ "$devops" == true ]]; then
192
+ printf '%s\n' "devops"
193
+ else
194
+ printf '%s\n' "generic"
195
+ fi
196
+ }
197
+
198
+ resolve_profile_path() {
199
+ local input="$1"
200
+ if [[ -f "$input" ]]; then
201
+ printf '%s\n' "$input"
202
+ return
203
+ fi
204
+
205
+ local candidate="$PLATFORM_ROOT/adapters/$input/profile.json"
206
+ if [[ -f "$candidate" ]]; then
207
+ printf '%s\n' "$candidate"
208
+ return
209
+ fi
210
+
211
+ echo "FAIL: profile not found: $input" >&2
212
+ echo "Known default profiles:" >&2
213
+ find "$PLATFORM_ROOT/adapters" -mindepth 2 -maxdepth 2 -name profile.json -print | sed "s#^$PLATFORM_ROOT/adapters/##; s#/profile.json##" | sort >&2
214
+ exit 1
215
+ }
216
+
217
+ resolve_package_source() {
218
+ if [[ -n "$PACKAGE_SOURCE" ]]; then
219
+ printf '%s\n' "$PACKAGE_SOURCE"
220
+ return
221
+ fi
222
+
223
+ local existing_settings="$PROJECT_PATH/.pi/settings.json"
224
+ if [[ -f "$existing_settings" ]]; then
225
+ local existing_source
226
+ existing_source="$(node --input-type=module - "$existing_settings" <<'NODE'
227
+ import fs from "node:fs";
228
+ const settings = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
229
+ const source = Array.isArray(settings.packages)
230
+ ? settings.packages.find((item) => typeof item === "string" && item.length > 0)
231
+ : undefined;
232
+ if (source) process.stdout.write(source);
233
+ NODE
234
+ )"
235
+ if [[ -n "$existing_source" ]]; then
236
+ printf '%s\n' "$existing_source"
237
+ return
238
+ fi
239
+ fi
240
+
241
+ echo "WARN: No exact package source provided." >&2
242
+ echo "WARN: .pi/settings.json will use local platform path; do not commit it for team use." >&2
243
+ printf '%s\n' "$PLATFORM_ROOT"
244
+ }
245
+
246
+ REQUESTED_PROFILE="$PROFILE_INPUT"
247
+ if [[ "$PROFILE_INPUT" == "auto" ]]; then
248
+ PROFILE_INPUT="$(detect_profile "$PROJECT_PATH")"
249
+ fi
250
+
251
+ PROFILE_PATH="$(resolve_profile_path "$PROFILE_INPUT")"
252
+ PACKAGE_SOURCE="$(resolve_package_source)"
253
+ node "$PLATFORM_ROOT/scripts/capability-catalog.mjs" validate-source --package-source "$PACKAGE_SOURCE" >/dev/null
254
+
255
+ mkdir -p "$PROJECT_PATH/.pi"
256
+
257
+ if [[ ! -f "$PROJECT_PATH/.mcp.json" ]]; then
258
+ cp "$PLATFORM_ROOT/templates/project/.mcp.json" "$PROJECT_PATH/.mcp.json"
259
+ fi
260
+
261
+ SETTINGS_PATH="$PROJECT_PATH/.pi/settings.json"
262
+ if [[ "$FORCE_SETTINGS" == true || ! -f "$SETTINGS_PATH" ]]; then
263
+ cp "$PLATFORM_ROOT/templates/project/.pi/settings.json" "$SETTINGS_PATH"
264
+ node --input-type=module - "$SETTINGS_PATH" "$PACKAGE_SOURCE" <<'NODE'
265
+ import fs from "node:fs";
266
+ const [settingsPath, packageSource] = process.argv.slice(2);
267
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
268
+ settings.packages = [packageSource];
269
+ fs.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
270
+ NODE
271
+ else
272
+ echo "SKIP: .pi/settings.json exists. Use --force-settings to replace."
273
+ fi
274
+
275
+ PROFILE_TARGET="$PROJECT_PATH/.pi/piagent-profile.json"
276
+ CAPABILITY_LOCK_TARGET="$PROJECT_PATH/.pi/piagent-profile.lock.json"
277
+ if [[ "$FORCE_PROFILE" == true || ! -f "$PROFILE_TARGET" ]]; then
278
+ apply_profile_args=(
279
+ "$PLATFORM_ROOT/scripts/capability-catalog.mjs"
280
+ apply-profile
281
+ --profile "$PROFILE_PATH"
282
+ --target "$PROFILE_TARGET"
283
+ --package-source "$PACKAGE_SOURCE"
284
+ )
285
+ if [[ "$FORCE_PROFILE" == true ]]; then
286
+ apply_profile_args+=(--force)
287
+ fi
288
+ node "${apply_profile_args[@]}" >/dev/null
289
+ else
290
+ echo "SKIP: .pi/piagent-profile.json exists. Use --force-profile to replace."
291
+ node "$PLATFORM_ROOT/scripts/capability-catalog.mjs" resolve \
292
+ --profile "$PROFILE_TARGET" \
293
+ --output "$CAPABILITY_LOCK_TARGET" \
294
+ --package-source "$PACKAGE_SOURCE" >/dev/null
295
+ fi
296
+
297
+ if [[ ! -f "$PROJECT_PATH/.pi/mcp.json" ]]; then
298
+ cp "$PLATFORM_ROOT/templates/project/.pi/mcp.json" "$PROJECT_PATH/.pi/mcp.json"
299
+ fi
300
+
301
+ if [[ ! -f "$PROJECT_PATH/.pi/.gitignore" ]]; then
302
+ if [[ -f "$PLATFORM_ROOT/templates/project/.pi/gitignore.template" ]]; then
303
+ cp "$PLATFORM_ROOT/templates/project/.pi/gitignore.template" "$PROJECT_PATH/.pi/.gitignore"
304
+ else
305
+ cp "$PLATFORM_ROOT/templates/project/.pi/.gitignore" "$PROJECT_PATH/.pi/.gitignore"
306
+ fi
307
+ fi
308
+
309
+ PROJECT_GITIGNORE="$PROJECT_PATH/.gitignore"
310
+ if [[ ! -f "$PROJECT_GITIGNORE" ]]; then
311
+ : > "$PROJECT_GITIGNORE"
312
+ fi
313
+ if ! grep -F "# Pi Agent Platform runtime" "$PROJECT_GITIGNORE" >/dev/null 2>&1; then
314
+ {
315
+ echo
316
+ echo "# Pi Agent Platform runtime"
317
+ echo ".pi-subagents/"
318
+ echo "progress.md"
319
+ } >> "$PROJECT_GITIGNORE"
320
+ fi
321
+
322
+ if [[ ! -f "$PROJECT_PATH/.pi/project-context.md" ]]; then
323
+ cp "$PLATFORM_ROOT/templates/project/.pi/project-context.md" "$PROJECT_PATH/.pi/project-context.md"
324
+ fi
325
+
326
+ if [[ ! -f "$PROJECT_PATH/.pi/context-index.json" ]]; then
327
+ cp "$PLATFORM_ROOT/templates/project/.pi/context-index.json" "$PROJECT_PATH/.pi/context-index.json"
328
+ fi
329
+
330
+ if [[ ! -f "$PROJECT_PATH/.pi/tech-stack.json" ]]; then
331
+ cp "$PLATFORM_ROOT/templates/project/.pi/tech-stack.json" "$PROJECT_PATH/.pi/tech-stack.json"
332
+ fi
333
+ mkdir -p "$PROJECT_PATH/.pi/tech-context"
334
+ if [[ ! -f "$PROJECT_PATH/.pi/tech-context/README.md" ]]; then
335
+ cp "$PLATFORM_ROOT/templates/project/.pi/tech-context/README.md" "$PROJECT_PATH/.pi/tech-context/README.md"
336
+ fi
337
+
338
+ mkdir -p "$PROJECT_PATH/.pi/memory"
339
+ if [[ ! -f "$PROJECT_PATH/.pi/memory/memory_summary.md" ]]; then
340
+ cp "$PLATFORM_ROOT/templates/project/.pi/memory/memory_summary.md" "$PROJECT_PATH/.pi/memory/memory_summary.md"
341
+ fi
342
+ if [[ ! -f "$PROJECT_PATH/.pi/memory/MEMORY.md" ]]; then
343
+ cp "$PLATFORM_ROOT/templates/project/.pi/memory/MEMORY.md" "$PROJECT_PATH/.pi/memory/MEMORY.md"
344
+ fi
345
+
346
+ if [[ "$SKIP_AGENTS" == false && ! -f "$PROJECT_PATH/AGENTS.md" ]]; then
347
+ cp "$PLATFORM_ROOT/templates/project/AGENTS.md" "$PROJECT_PATH/AGENTS.md"
348
+ fi
349
+
350
+ if [[ "$SKIP_REVIEW" == false && ! -f "$PROJECT_PATH/REVIEW_GUIDELINES.md" ]]; then
351
+ cp "$PLATFORM_ROOT/templates/project/REVIEW_GUIDELINES.md" "$PROJECT_PATH/REVIEW_GUIDELINES.md"
352
+ fi
353
+
354
+ echo "Project initialized:"
355
+ echo " project: $PROJECT_PATH"
356
+ echo " requestedProfile: $REQUESTED_PROFILE"
357
+ echo " resolvedProfile: $PROFILE_INPUT"
358
+ echo " profile: $PROFILE_TARGET"
359
+ echo " capabilityLock: $CAPABILITY_LOCK_TARGET"
360
+ echo " profileSource: $PROFILE_PATH"
361
+ echo " packageSource: $PACKAGE_SOURCE"
362
+ echo
363
+ echo "Next:"
364
+ echo " cd \"$PROJECT_PATH\""
365
+ echo " pi"
366
+ echo " /login # first time only"
367
+ echo " /onboard-project # first project-read after model selection"
368
+ echo " /context-index # inspect compact project context index"
369
+ echo " /memory-policy # inspect project memory policy when needed"