pan-stack-cli 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +572 -23
- package/bin/pan.js +206 -20
- package/lib/ai-skills.js +829 -0
- package/lib/create.js +22 -4
- package/lib/discover.js +69 -2
- package/lib/git/git-checkout.js +395 -0
- package/lib/git/git-clone.js +163 -28
- package/lib/git/git-fetch-all.js +100 -33
- package/lib/git/git-grv.js +68 -37
- package/lib/git/git-gst.js +97 -26
- package/lib/git/git-mr.js +546 -0
- package/lib/git/git-pull.js +104 -43
- package/lib/git/git-push.js +150 -41
- package/lib/git/git-sync-main.js +334 -0
- package/lib/git/helpers/bulk-report.js +202 -0
- package/lib/git/helpers/cli.js +41 -5
- package/lib/git/helpers/colors.js +19 -0
- package/lib/git/helpers/git.js +74 -0
- package/lib/git/helpers/manifest.js +93 -0
- package/lib/git/helpers/repo-select.js +416 -0
- package/lib/git/helpers/repo-summary.js +161 -0
- package/lib/i18n.js +61 -0
- package/lib/inquirer-theme.js +20 -0
- package/lib/language.js +121 -0
- package/lib/languages.js +14 -0
- package/lib/manifest.js +115 -14
- package/lib/menu-prompt.js +201 -0
- package/lib/menu.js +134 -0
- package/lib/messages.js +647 -0
- package/lib/run-command.js +173 -52
- package/lib/select.js +0 -0
- package/lib/serve.js +75 -0
- package/lib/skill-registry-client.js +173 -0
- package/lib/stats/aggregate.js +51 -0
- package/lib/stats/identity.js +20 -0
- package/lib/stats/index.js +189 -0
- package/lib/stats/parse.js +46 -0
- package/lib/stats/range.js +38 -0
- package/lib/stats/report.js +264 -0
- package/lib/stats/review.js +157 -0
- package/lib/stats/scan.js +92 -0
- package/lib/stats/serve.js +25 -0
- package/lib/ui/api.js +202 -0
- package/lib/ui/clone.js +115 -0
- package/lib/ui/config.js +182 -0
- package/lib/ui/diff.js +128 -0
- package/lib/ui/export-gate.js +79 -0
- package/lib/ui/gen-react-mobile-code.js +6 -0
- package/lib/ui/gen-react-web-code.js +75 -0
- package/lib/ui/index.js +89 -0
- package/lib/ui/init.js +252 -0
- package/lib/ui/login.js +128 -0
- package/lib/ui/logout.js +22 -0
- package/lib/ui/palettes.js +103 -0
- package/lib/ui/published-html.js +24 -0
- package/lib/ui/sync.js +137 -0
- package/lib/ui/workspace.js +213 -0
- package/package.json +10 -3
- package/templates/workspace/.claude/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.codex/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.cursor/skills/writing-guidelines/SKILL.md +0 -48
- package/templates/workspace/.pan.yaml +0 -25
- package/templates/workspace/.project.yaml +0 -15
- package/templates/workspace/README.md +0 -35
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// lib/ui/workspace.js — monorepo scan cho `pan ui` (§4.7): tìm workspace root, quét
|
|
2
|
+
// mọi app.panui.yaml (KHÔNG dùng `repositories:` trong .project.yaml), chọn dự án,
|
|
3
|
+
// đọc/ghi khối `backend: { id, apiUrl }` trong app.panui.yaml.
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { findNearestProjectManifest } = require("../discover");
|
|
7
|
+
const { menuSelect } = require("../menu-prompt");
|
|
8
|
+
const { t } = require("../i18n");
|
|
9
|
+
|
|
10
|
+
const IGNORE_DIRS = new Set(["node_modules", ".git", "dist", ".codegen-check"]);
|
|
11
|
+
const MAX_DEPTH = 5;
|
|
12
|
+
const APP_YAML_NAME = "app.panui.yaml";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tìm workspace root bằng walkUp `.project.yaml` (tái dùng lib/discover.js).
|
|
16
|
+
* @param {string} [cwd]
|
|
17
|
+
* @returns {string|null}
|
|
18
|
+
*/
|
|
19
|
+
function findWorkspaceRoot(cwd = process.cwd()) {
|
|
20
|
+
const manifestPath = findNearestProjectManifest(cwd);
|
|
21
|
+
return manifestPath ? path.dirname(manifestPath) : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Quét đệ quy tối đa MAX_DEPTH cấp từ root, tìm mọi app.panui.yaml.
|
|
26
|
+
* @param {string} root
|
|
27
|
+
* @returns {Array<{ appYamlPath: string, dir: string, name: string }>}
|
|
28
|
+
*/
|
|
29
|
+
function scanPanuiProjects(root) {
|
|
30
|
+
const results = [];
|
|
31
|
+
|
|
32
|
+
function walk(dir, depth) {
|
|
33
|
+
if (depth > MAX_DEPTH) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (entry.name.startsWith(".") && entry.name !== ".") {
|
|
45
|
+
// vẫn cho phép quét vào các thư mục ẩn không nằm trong IGNORE_DIRS? Không —
|
|
46
|
+
// giữ đơn giản: bỏ mọi thư mục ẩn để tránh .git, .cache, ... rơi vào quét.
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const fullPath = path.join(dir, entry.name);
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
if (IGNORE_DIRS.has(entry.name)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
walk(fullPath, depth + 1);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (entry.isFile() && entry.name === APP_YAML_NAME) {
|
|
60
|
+
results.push({
|
|
61
|
+
appYamlPath: fullPath,
|
|
62
|
+
dir,
|
|
63
|
+
name: path.basename(dir),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
walk(root, 0);
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {Array<{ appYamlPath: string, dir: string, name: string }>} projects
|
|
75
|
+
* @returns {Promise<{ appYamlPath: string, dir: string, name: string }>}
|
|
76
|
+
*/
|
|
77
|
+
async function selectProject(projects) {
|
|
78
|
+
if (!projects || projects.length === 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Không tìm thấy dự án panui nào (không có ${APP_YAML_NAME} trong workspace, quét tối đa ${MAX_DEPTH} cấp).`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (projects.length === 1) {
|
|
84
|
+
return projects[0];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const message = `Chọn dự án panui (${projects.length} dự án):`;
|
|
88
|
+
const choices = projects.map((p) => ({
|
|
89
|
+
name: `${p.name} (${path.relative(process.cwd(), p.appYamlPath)})`,
|
|
90
|
+
value: p.appYamlPath,
|
|
91
|
+
}));
|
|
92
|
+
const hotkeys = [{ key: "x", label: t("EXIT_LABEL"), value: "__exit__" }];
|
|
93
|
+
|
|
94
|
+
let choice;
|
|
95
|
+
try {
|
|
96
|
+
choice = await menuSelect({ message, choices, hotkeys });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (String(err && err.message) !== "no-core") throw err;
|
|
99
|
+
const inq = await import("@inquirer/prompts").catch(() => null);
|
|
100
|
+
if (!inq || typeof inq.select !== "function") {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"Nhiều dự án panui — cần TTY để chọn (không tìm thấy @inquirer/prompts)."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
choice = await inq.select({
|
|
106
|
+
message,
|
|
107
|
+
choices: [...choices, new inq.Separator(), { name: `x ${t("EXIT_LABEL")}`, value: "__exit__" }],
|
|
108
|
+
loop: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (choice === "__exit__") {
|
|
112
|
+
console.log(t("EXITED"));
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
return projects.find((p) => p.appYamlPath === choice);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Đọc khối `backend: { id, apiUrl }` trong app.panui.yaml (parser tối thiểu —
|
|
120
|
+
* chỉ hỗ trợ khối phẳng 2 dòng con `id:`/`apiUrl:` thụt lề 2 khoảng trắng).
|
|
121
|
+
* @param {string} appYamlPath
|
|
122
|
+
* @returns {{ id: string, apiUrl: string } | null}
|
|
123
|
+
*/
|
|
124
|
+
function readBackendId(appYamlPath) {
|
|
125
|
+
if (!fs.existsSync(appYamlPath)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const lines = fs.readFileSync(appYamlPath, "utf8").split(/\r?\n/);
|
|
129
|
+
let inBackend = false;
|
|
130
|
+
const result = {};
|
|
131
|
+
for (const rawLine of lines) {
|
|
132
|
+
if (!rawLine.trim()) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
136
|
+
const text = rawLine.trim();
|
|
137
|
+
if (indent === 0) {
|
|
138
|
+
inBackend = text.replace(/:$/, "") === "backend";
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (inBackend && indent === 2) {
|
|
142
|
+
const idx = text.indexOf(":");
|
|
143
|
+
if (idx < 0) continue;
|
|
144
|
+
const key = text.slice(0, idx).trim();
|
|
145
|
+
let value = text.slice(idx + 1).trim();
|
|
146
|
+
if (
|
|
147
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
148
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
149
|
+
) {
|
|
150
|
+
value = value.slice(1, -1);
|
|
151
|
+
}
|
|
152
|
+
if (key === "id" || key === "apiUrl") {
|
|
153
|
+
result[key] = value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return result.id || result.apiUrl ? result : null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Ghi/cập nhật khối `backend: { id, apiUrl }` trong app.panui.yaml, giữ nguyên
|
|
162
|
+
* mọi nội dung khác của file (thay thế khối backend cũ nếu có, nếu không thì
|
|
163
|
+
* append vào cuối file).
|
|
164
|
+
* @param {string} appYamlPath
|
|
165
|
+
* @param {{ id: string, apiUrl: string }} backend
|
|
166
|
+
*/
|
|
167
|
+
function writeBackendId(appYamlPath, { id, apiUrl }) {
|
|
168
|
+
const original = fs.existsSync(appYamlPath)
|
|
169
|
+
? fs.readFileSync(appYamlPath, "utf8")
|
|
170
|
+
: "";
|
|
171
|
+
const lines = original.length ? original.split(/\r?\n/) : [];
|
|
172
|
+
|
|
173
|
+
const newBlock = ["backend:", ` id: ${id}`, ` apiUrl: ${apiUrl}`];
|
|
174
|
+
|
|
175
|
+
let start = -1;
|
|
176
|
+
let end = -1;
|
|
177
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
178
|
+
const trimmed = lines[i].trim();
|
|
179
|
+
const indent = lines[i].length - lines[i].trimStart().length;
|
|
180
|
+
if (indent === 0 && trimmed.replace(/:$/, "") === "backend") {
|
|
181
|
+
start = i;
|
|
182
|
+
end = i + 1;
|
|
183
|
+
while (end < lines.length) {
|
|
184
|
+
const nextIndent = lines[end].length - lines[end].trimStart().length;
|
|
185
|
+
if (!lines[end].trim() || nextIndent > 0) {
|
|
186
|
+
end += 1;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let updatedLines;
|
|
196
|
+
if (start >= 0) {
|
|
197
|
+
updatedLines = [...lines.slice(0, start), ...newBlock, ...lines.slice(end)];
|
|
198
|
+
} else {
|
|
199
|
+
updatedLines = lines.length ? [...lines, "", ...newBlock] : [...newBlock];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fs.mkdirSync(path.dirname(appYamlPath), { recursive: true });
|
|
203
|
+
fs.writeFileSync(appYamlPath, `${updatedLines.join("\n")}\n`, "utf8");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
APP_YAML_NAME,
|
|
208
|
+
findWorkspaceRoot,
|
|
209
|
+
scanPanuiProjects,
|
|
210
|
+
selectProject,
|
|
211
|
+
readBackendId,
|
|
212
|
+
writeBackendId,
|
|
213
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pan-stack-cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "PAN Stack CLI — multi-repo git and project commands from .project.yaml",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
11
|
-
"lib"
|
|
12
|
-
"templates"
|
|
11
|
+
"lib"
|
|
13
12
|
],
|
|
14
13
|
"engines": {
|
|
15
14
|
"node": ">=18"
|
|
16
15
|
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@inquirer/core": "^11.2.1",
|
|
18
|
+
"@inquirer/prompts": "^8.5.2",
|
|
19
|
+
"serve": "^14.2.4"
|
|
20
|
+
},
|
|
17
21
|
"keywords": [
|
|
18
22
|
"pan-stack",
|
|
19
23
|
"monorepo",
|
|
@@ -24,5 +28,8 @@
|
|
|
24
28
|
"type": "git",
|
|
25
29
|
"url": "https://github.com/doiya46/pan-stack.git",
|
|
26
30
|
"directory": ".pan/pan-stack-cli"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@inquirer/testing": "^3.3.9"
|
|
27
34
|
}
|
|
28
35
|
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: writing-guidelines
|
|
3
|
-
description: Review docs/prose for Writing Guidelines compliance. Use when asked to "review my docs", "check writing style", "audit prose", "review docs voice and tone", or "check this page against the writing handbook".
|
|
4
|
-
metadata:
|
|
5
|
-
author: vercel
|
|
6
|
-
version: "1.0.0"
|
|
7
|
-
argument-hint: <file-or-pattern>
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Writing Guidelines
|
|
11
|
-
|
|
12
|
-
Review files for compliance with Writing Guidelines.
|
|
13
|
-
|
|
14
|
-
## How It Works
|
|
15
|
-
|
|
16
|
-
1. Fetch the latest guidelines from the source URL below
|
|
17
|
-
2. Read the specified files (or prompt user for files/pattern)
|
|
18
|
-
3. Check against all rules in the fetched guidelines
|
|
19
|
-
4. Output findings in the terse `file:line` format
|
|
20
|
-
|
|
21
|
-
## Guidelines Source
|
|
22
|
-
|
|
23
|
-
Fetch fresh guidelines before each review:
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
https://raw.githubusercontent.com/vercel-labs/writing-guidelines/main/command.md
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and output format instructions.
|
|
30
|
-
|
|
31
|
-
## Local Markdown Rules
|
|
32
|
-
|
|
33
|
-
Also check generated or edited Markdown against local markdownlint conventions:
|
|
34
|
-
|
|
35
|
-
- Keep at most one blank line between blocks. Do not create multiple consecutive blank lines. This avoids `MD012/no-multiple-blanks`.
|
|
36
|
-
- Use language tags on fenced code blocks.
|
|
37
|
-
- Before finishing edits, scan changed Markdown files for repeated blank lines.
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
When a user provides a file or pattern argument:
|
|
42
|
-
1. Fetch guidelines from the source URL above
|
|
43
|
-
2. Read the specified files
|
|
44
|
-
3. Apply all rules from the fetched guidelines
|
|
45
|
-
4. Apply the local Markdown rules above
|
|
46
|
-
5. Output findings using the format specified in the guidelines
|
|
47
|
-
|
|
48
|
-
If no files specified, ask the user which files to review.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: writing-guidelines
|
|
3
|
-
description: Review docs/prose for Writing Guidelines compliance. Use when asked to "review my docs", "check writing style", "audit prose", "review docs voice and tone", or "check this page against the writing handbook".
|
|
4
|
-
metadata:
|
|
5
|
-
author: vercel
|
|
6
|
-
version: "1.0.0"
|
|
7
|
-
argument-hint: <file-or-pattern>
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Writing Guidelines
|
|
11
|
-
|
|
12
|
-
Review files for compliance with Writing Guidelines.
|
|
13
|
-
|
|
14
|
-
## How It Works
|
|
15
|
-
|
|
16
|
-
1. Fetch the latest guidelines from the source URL below
|
|
17
|
-
2. Read the specified files (or prompt user for files/pattern)
|
|
18
|
-
3. Check against all rules in the fetched guidelines
|
|
19
|
-
4. Output findings in the terse `file:line` format
|
|
20
|
-
|
|
21
|
-
## Guidelines Source
|
|
22
|
-
|
|
23
|
-
Fetch fresh guidelines before each review:
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
https://raw.githubusercontent.com/vercel-labs/writing-guidelines/main/command.md
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and output format instructions.
|
|
30
|
-
|
|
31
|
-
## Local Markdown Rules
|
|
32
|
-
|
|
33
|
-
Also check generated or edited Markdown against local markdownlint conventions:
|
|
34
|
-
|
|
35
|
-
- Keep at most one blank line between blocks. Do not create multiple consecutive blank lines. This avoids `MD012/no-multiple-blanks`.
|
|
36
|
-
- Use language tags on fenced code blocks.
|
|
37
|
-
- Before finishing edits, scan changed Markdown files for repeated blank lines.
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
When a user provides a file or pattern argument:
|
|
42
|
-
1. Fetch guidelines from the source URL above
|
|
43
|
-
2. Read the specified files
|
|
44
|
-
3. Apply all rules from the fetched guidelines
|
|
45
|
-
4. Apply the local Markdown rules above
|
|
46
|
-
5. Output findings using the format specified in the guidelines
|
|
47
|
-
|
|
48
|
-
If no files specified, ask the user which files to review.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: writing-guidelines
|
|
3
|
-
description: Review docs/prose for Writing Guidelines compliance. Use when asked to "review my docs", "check writing style", "audit prose", "review docs voice and tone", or "check this page against the writing handbook".
|
|
4
|
-
metadata:
|
|
5
|
-
author: vercel
|
|
6
|
-
version: "1.0.0"
|
|
7
|
-
argument-hint: <file-or-pattern>
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Writing Guidelines
|
|
11
|
-
|
|
12
|
-
Review files for compliance with Writing Guidelines.
|
|
13
|
-
|
|
14
|
-
## How It Works
|
|
15
|
-
|
|
16
|
-
1. Fetch the latest guidelines from the source URL below
|
|
17
|
-
2. Read the specified files (or prompt user for files/pattern)
|
|
18
|
-
3. Check against all rules in the fetched guidelines
|
|
19
|
-
4. Output findings in the terse `file:line` format
|
|
20
|
-
|
|
21
|
-
## Guidelines Source
|
|
22
|
-
|
|
23
|
-
Fetch fresh guidelines before each review:
|
|
24
|
-
|
|
25
|
-
```text
|
|
26
|
-
https://raw.githubusercontent.com/vercel-labs/writing-guidelines/main/command.md
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Use WebFetch to retrieve the latest rules. The fetched content contains all the rules and output format instructions.
|
|
30
|
-
|
|
31
|
-
## Local Markdown Rules
|
|
32
|
-
|
|
33
|
-
Also check generated or edited Markdown against local markdownlint conventions:
|
|
34
|
-
|
|
35
|
-
- Keep at most one blank line between blocks. Do not create multiple consecutive blank lines. This avoids `MD012/no-multiple-blanks`.
|
|
36
|
-
- Use language tags on fenced code blocks.
|
|
37
|
-
- Before finishing edits, scan changed Markdown files for repeated blank lines.
|
|
38
|
-
|
|
39
|
-
## Usage
|
|
40
|
-
|
|
41
|
-
When a user provides a file or pattern argument:
|
|
42
|
-
1. Fetch guidelines from the source URL above
|
|
43
|
-
2. Read the specified files
|
|
44
|
-
3. Apply all rules from the fetched guidelines
|
|
45
|
-
4. Apply the local Markdown rules above
|
|
46
|
-
5. Output findings using the format specified in the guidelines
|
|
47
|
-
|
|
48
|
-
If no files specified, ask the user which files to review.
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# Workspace-level commands. Per-repo commands live in apps/*/.pan.yaml
|
|
2
|
-
commands:
|
|
3
|
-
install:
|
|
4
|
-
alias: i
|
|
5
|
-
description: Install dependencies across cloned repositories
|
|
6
|
-
shell: |
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
root="${PAN_WORKSPACE_ROOT}"
|
|
9
|
-
for dir in apps/web apps/api; do
|
|
10
|
-
[[ -d "$root/$dir" ]] || { echo "Missing: $root/$dir (run 'pan clone' first?)" >&2; exit 1; }
|
|
11
|
-
echo "-> install $dir"
|
|
12
|
-
(cd "$root/$dir" && npm install)
|
|
13
|
-
done
|
|
14
|
-
echo "Done."
|
|
15
|
-
|
|
16
|
-
test:
|
|
17
|
-
default: web
|
|
18
|
-
description: Run tests (web | api)
|
|
19
|
-
targets:
|
|
20
|
-
web:
|
|
21
|
-
cwd: apps/web
|
|
22
|
-
exec: npm test
|
|
23
|
-
api:
|
|
24
|
-
cwd: apps/api
|
|
25
|
-
exec: npm test
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
project: __PROJECT_NAME__
|
|
2
|
-
managed_by: your-team
|
|
3
|
-
|
|
4
|
-
stack:
|
|
5
|
-
repo: repo_url_to_be_filled
|
|
6
|
-
|
|
7
|
-
repositories:
|
|
8
|
-
- name: bookshop-web
|
|
9
|
-
description: Customer-facing web application
|
|
10
|
-
url: repo_url_to_be_filled
|
|
11
|
-
path: apps/web
|
|
12
|
-
- name: bookshop-api
|
|
13
|
-
description: REST API service
|
|
14
|
-
url: repo_url_to_be_filled
|
|
15
|
-
path: apps/api
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# __PROJECT_NAME__
|
|
2
|
-
|
|
3
|
-
PAN Stack workspace shell for the Bookshop example project.
|
|
4
|
-
|
|
5
|
-
This repository is the **coordination layer** — not the product source tree. It holds
|
|
6
|
-
agent skills, workspace manifests, and `pan` commands. Product code lives in the
|
|
7
|
-
repositories listed in `.project.yaml`.
|
|
8
|
-
|
|
9
|
-
## Layout
|
|
10
|
-
|
|
11
|
-
```text
|
|
12
|
-
__PROJECT_NAME__/
|
|
13
|
-
.project.yaml # Workspace manifest (repository list)
|
|
14
|
-
.pan.yaml # Workspace-level pan commands
|
|
15
|
-
.claude/skills/ # Claude Code skills
|
|
16
|
-
.cursor/skills/ # Cursor agent skills
|
|
17
|
-
.codex/skills/ # Codex agent skills
|
|
18
|
-
apps/
|
|
19
|
-
web/ # bookshop-web (after pan clone)
|
|
20
|
-
api/ # bookshop-api (after pan clone)
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Quick start
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Edit repository URLs in .project.yaml, then:
|
|
27
|
-
pan clone
|
|
28
|
-
pan install
|
|
29
|
-
pan test web
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Agent skills
|
|
33
|
-
|
|
34
|
-
All three agent folders ship with the shared `writing-guidelines` skill. Add more
|
|
35
|
-
skills under each `.claude`, `.cursor`, and `.codex` directory as your team grows.
|