@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.
- package/.gitattributes +7 -0
- package/AGENTS.md +31 -0
- package/CHANGELOG.md +296 -0
- package/CODE_OF_CONDUCT.md +36 -0
- package/CONTRIBUTING.md +66 -0
- package/LICENSE +21 -0
- package/README.md +588 -0
- package/SECURITY.md +38 -0
- package/adapters/backend-api/profile.json +88 -0
- package/adapters/be-readonly-fe/profile.json +120 -0
- package/adapters/data/profile.json +88 -0
- package/adapters/devops/profile.json +92 -0
- package/adapters/docs/profile.json +74 -0
- package/adapters/fullstack/profile.json +94 -0
- package/adapters/generic/profile.json +95 -0
- package/adapters/mobile/profile.json +87 -0
- package/adapters/node-typescript/profile.json +76 -0
- package/adapters/python/profile.json +75 -0
- package/adapters/web-frontend/profile.json +101 -0
- package/catalog/capabilities.json +206 -0
- package/docs/architecture.md +64 -0
- package/docs/auto-delegation-policy.md +192 -0
- package/docs/capability-packs.md +234 -0
- package/docs/command-reference-vietnamese.md +484 -0
- package/docs/context-window-policy.md +85 -0
- package/docs/distribution-standard.md +229 -0
- package/docs/herdr-workflow.md +58 -0
- package/docs/mcp-and-tools.md +316 -0
- package/docs/memory-policy.md +164 -0
- package/docs/model-options.md +165 -0
- package/docs/oauth-providers.md +53 -0
- package/docs/operator-manual-vietnamese.md +1080 -0
- package/docs/package-architecture-notes.md +101 -0
- package/docs/project-adapters.md +174 -0
- package/docs/project-onboarding.md +135 -0
- package/docs/publishing-for-teams.md +64 -0
- package/docs/quality-benchmark.md +74 -0
- package/docs/quickstart-vietnamese.md +350 -0
- package/docs/release-install-policy.md +223 -0
- package/docs/runtime-harness-standard.md +118 -0
- package/docs/runtime-policy-design.md +132 -0
- package/docs/runtime-quality-baseline.md +257 -0
- package/docs/security-redaction-benchmark.md +56 -0
- package/docs/security-threat-model.md +66 -0
- package/docs/subagent-orchestration-capabilities.md +104 -0
- package/docs/subagents-and-multiagent.md +346 -0
- package/docs/task-implementation-contract.md +200 -0
- package/docs/team-onboarding.md +333 -0
- package/docs/usage-observability.md +130 -0
- package/docs/vercel-docs-site.md +115 -0
- package/docs/workflow-recipes.md +125 -0
- package/docs-site/assets/piagent-logo.svg +35 -0
- package/docs-site/favicon.svg +18 -0
- package/docs-site/index.html +1613 -0
- package/docs-site/vercel.json +24 -0
- package/evals/fixtures/action-proposal.invalid.json +34 -0
- package/evals/fixtures/action-proposal.valid.json +34 -0
- package/evals/fixtures/capability-pack.invalid.json +123 -0
- package/evals/fixtures/capability-pack.valid.json +123 -0
- package/evals/fixtures/capability-recipe.invalid.json +81 -0
- package/evals/fixtures/capability-recipe.valid.json +82 -0
- package/evals/fixtures/context-index.invalid.json +25 -0
- package/evals/fixtures/context-index.valid.json +25 -0
- package/evals/fixtures/eval-scenario.invalid.json +40 -0
- package/evals/fixtures/eval-scenario.valid.json +45 -0
- package/evals/fixtures/project-profile.invalid.json +88 -0
- package/evals/fixtures/project-profile.valid.json +95 -0
- package/evals/fixtures/task-contract.invalid.json +73 -0
- package/evals/fixtures/task-contract.valid.json +74 -0
- package/evals/golden/enforcement-decisions.json +329 -0
- package/evals/scenarios/capability-resolution.json +45 -0
- package/package.json +132 -0
- package/packages/piagent-core/README.md +147 -0
- package/packages/piagent-core/capabilities/capability-core.js +1023 -0
- package/packages/piagent-core/capabilities/capability-sources.js +265 -0
- package/packages/piagent-core/extensions/guard-io.js +45 -0
- package/packages/piagent-core/extensions/guard-shell-analysis.ts +558 -0
- package/packages/piagent-core/extensions/guard-types.ts +406 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +5760 -0
- package/packages/piagent-core/extensions/policy-core.js +1070 -0
- package/packages/piagent-core/extensions/redaction-core.js +4 -0
- package/packages/piagent-core/extensions/runtime-evidence.js +215 -0
- package/packages/piagent-core/package.json +46 -0
- package/packages/piagent-core/policies/base-policy.json +249 -0
- package/packages/piagent-core/prompts/be-to-fe.md +82 -0
- package/packages/piagent-core/prompts/commit.md +27 -0
- package/packages/piagent-core/prompts/discuss.md +35 -0
- package/packages/piagent-core/prompts/memory-policy.md +52 -0
- package/packages/piagent-core/prompts/model-options.md +68 -0
- package/packages/piagent-core/prompts/onboard-project.md +147 -0
- package/packages/piagent-core/prompts/piagent-commands.md +68 -0
- package/packages/piagent-core/prompts/plan.md +35 -0
- package/packages/piagent-core/prompts/platform-improve.md +69 -0
- package/packages/piagent-core/prompts/pr.md +26 -0
- package/packages/piagent-core/prompts/review.md +38 -0
- package/packages/piagent-core/prompts/scout.md +50 -0
- package/packages/piagent-core/prompts/task.md +69 -0
- package/packages/piagent-core/security/sensitive-data.js +187 -0
- package/packages/piagent-core/skills/piagent-ops/SKILL.md +44 -0
- package/packages/piagent-core/skills/piagent-source-cache/SKILL.md +59 -0
- package/packages/piagent-core/skills/piagent-source-cache/checkout-source-repo.sh +144 -0
- package/packages/piagent-core/subagents/piagent-oracle.md +29 -0
- package/packages/piagent-core/subagents/piagent-planner.md +46 -0
- package/packages/piagent-core/subagents/piagent-reviewer.md +48 -0
- package/packages/piagent-core/subagents/piagent-scout.md +37 -0
- package/packages/piagent-core/subagents/piagent-worker.md +44 -0
- package/packs/engineering-base/pack.json +123 -0
- package/packs/engineering-base/recipes/bounded-change.json +82 -0
- package/packs/web-delivery/pack.json +72 -0
- package/packs/web-delivery/recipes/verified-web-change.json +57 -0
- package/schemas/action-proposal.schema.json +90 -0
- package/schemas/capability-pack.schema.json +190 -0
- package/schemas/capability-recipe.schema.json +72 -0
- package/schemas/context-index.schema.json +258 -0
- package/schemas/eval-scenario.schema.json +67 -0
- package/schemas/project-profile.schema.json +555 -0
- package/schemas/task-contract.schema.json +314 -0
- package/scripts/audit-runtime-host.sh +78 -0
- package/scripts/bootstrap-local.sh +25 -0
- package/scripts/capability-catalog.mjs +290 -0
- package/scripts/check-runtime-advisories.mjs +114 -0
- package/scripts/configure-mcp.sh +301 -0
- package/scripts/configure-model-scope.sh +143 -0
- package/scripts/configure-subagents.sh +241 -0
- package/scripts/import-agent-instructions.mjs +199 -0
- package/scripts/init-project.sh +369 -0
- package/scripts/install-global.sh +532 -0
- package/scripts/link-project.sh +25 -0
- package/scripts/migrate-project-state.mjs +176 -0
- package/scripts/pi-auto.sh +79 -0
- package/scripts/pi-model-catalog.sh +154 -0
- package/scripts/pi-session-stats.sh +135 -0
- package/scripts/piagent-cli.mjs +53 -0
- package/scripts/profile-doctor.sh +238 -0
- package/scripts/quality-benchmark.sh +185 -0
- package/scripts/redaction-benchmark.mjs +227 -0
- package/scripts/runtime-policy-smoke.sh +48 -0
- package/scripts/setup.sh +425 -0
- package/scripts/team-doctor.sh +461 -0
- package/scripts/verify-local.sh +540 -0
- package/scripts/verify-release-identity.mjs +85 -0
- package/scripts/verify-vercel-link.mjs +64 -0
- package/templates/global/mcp.json +9 -0
- package/templates/global/settings.json +37 -0
- package/templates/project/.mcp.json +9 -0
- package/templates/project/.pi/context-index.json +25 -0
- package/templates/project/.pi/gitignore.template +22 -0
- package/templates/project/.pi/mcp.json +9 -0
- package/templates/project/.pi/memory/MEMORY.md +11 -0
- package/templates/project/.pi/memory/memory_summary.md +9 -0
- package/templates/project/.pi/piagent-profile.json +107 -0
- package/templates/project/.pi/project-context.md +30 -0
- package/templates/project/.pi/settings.json +17 -0
- package/templates/project/.pi/task-contract.template.json +74 -0
- package/templates/project/.pi/tech-context/README.md +7 -0
- package/templates/project/.pi/tech-stack.json +10 -0
- package/templates/project/AGENTS.md +34 -0
- package/templates/project/REVIEW_GUIDELINES.md +23 -0
- 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"
|