@k0t0vich/meta-agents-template 0.1.6 → 0.1.7
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/CHANGELOG.md +10 -0
- package/README.md +42 -44
- package/package.json +1 -1
- package/src/cli.mjs +49 -1
- package/src/init.mjs +262 -30
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.7 - 2026-03-20
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Switched `meta-agents init` to non-destructive bootstrap: root now gets only `.meta-agents/config/*`, while automation rules are consumed from `node_modules/@k0t0vich/meta-agents-template`.
|
|
9
|
+
- Prevented accidental overwrite of existing project `agents.md`: init now appends/updates only a managed links block instead of replacing full content.
|
|
10
|
+
- Reworked package setup in existing projects: `package.json` is merged (not replaced), preserving existing scripts while adding `meta:*` commands and `devDependencies` entry.
|
|
11
|
+
- Added runtime package self-install during init when package files are not present locally (safe `npx` path).
|
|
12
|
+
- Updated public README with robust one-line onboarding for npm URL/package-name prompts and new post-init layout.
|
|
13
|
+
- Updated local post-publish smoke to validate the new config-only root flow.
|
|
14
|
+
|
|
5
15
|
## 0.1.6 - 2026-03-20
|
|
6
16
|
|
|
7
17
|
### Changed
|
package/README.md
CHANGED
|
@@ -9,17 +9,46 @@
|
|
|
9
9
|
- командный протокол работы с задачами и спринтами;
|
|
10
10
|
- выбор backend-трекера (`github`, `mcp`, `local`, `custom`).
|
|
11
11
|
|
|
12
|
-
## Установка
|
|
12
|
+
## Установка (надёжно из любой папки)
|
|
13
|
+
Если целевая папка пустая или в ней нет `package.json`, сначала зафиксируй локальный npm-проект:
|
|
14
|
+
|
|
13
15
|
```bash
|
|
16
|
+
mkdir -p my-project
|
|
17
|
+
cd my-project
|
|
18
|
+
npm init -y
|
|
14
19
|
npm i -D @k0t0vich/meta-agents-template
|
|
15
|
-
npx meta-agents init
|
|
20
|
+
npx meta-agents init . --yes --tracker github
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Альтернатива через `npx` (init сам добавит пакет в `devDependencies`, если его нет локально):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mkdir -p my-project
|
|
27
|
+
cd my-project
|
|
28
|
+
npx -y @k0t0vich/meta-agents-template init . --yes --tracker github
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Важно:
|
|
32
|
+
- если пользователь дал URL `https://www.npmjs.com/package/@k0t0vich/meta-agents-template`, извлекай имя пакета `@k0t0vich/meta-agents-template` и ставь именно его;
|
|
33
|
+
- не запускай `npm i -D ...` в папке без `package.json`, иначе npm может установить пакет в родительский проект.
|
|
34
|
+
- `meta-agents init` по умолчанию работает non-destructive: в корне создаёт/обновляет только `.meta-agents/config/*`, аккуратно merge-ит `meta:*` scripts в `package.json` и добавляет ссылочный блок в `agents.md` вместо полной перезаписи.
|
|
35
|
+
- `--force` нужен только если надо перезаписать уже существующие `.meta-agents/config/*` и мигрировать legacy `meta:*` scripts.
|
|
36
|
+
|
|
37
|
+
## One-line для агентного запуска
|
|
38
|
+
Если пользователь пишет только:
|
|
39
|
+
- `поставь https://www.npmjs.com/package/@k0t0vich/meta-agents-template`
|
|
40
|
+
- `поставь @k0t0vich/meta-agents-template`
|
|
41
|
+
|
|
42
|
+
используй безопасный one-liner:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
mkdir -p my-project && cd my-project && npm init -y && npm i -D @k0t0vich/meta-agents-template && npx meta-agents init . --yes --tracker github
|
|
16
46
|
```
|
|
17
47
|
|
|
18
|
-
|
|
48
|
+
Проверка, что шаблон и правила действительно развернуты:
|
|
19
49
|
|
|
20
50
|
```bash
|
|
21
|
-
|
|
22
|
-
npx meta-agents init my-project
|
|
51
|
+
test -f agents.md && test -f .meta-agents/config/project-context.yaml && test -f node_modules/@k0t0vich/meta-agents-template/agents.md && echo "meta-agents template ready"
|
|
23
52
|
```
|
|
24
53
|
|
|
25
54
|
## Диалог при `init`
|
|
@@ -33,55 +62,24 @@ npx meta-agents init my-project
|
|
|
33
62
|
## Структура проекта после `init`
|
|
34
63
|
```text
|
|
35
64
|
my-project/
|
|
36
|
-
.github/
|
|
37
|
-
workflows/
|
|
38
|
-
gitflow-lite-verify.yml
|
|
39
65
|
package.json
|
|
40
|
-
|
|
66
|
+
package-lock.json
|
|
41
67
|
agents.md
|
|
42
|
-
tracker-command-template.md
|
|
43
68
|
.meta-agents/
|
|
44
69
|
config/
|
|
45
70
|
system.yaml
|
|
46
71
|
roles.yaml
|
|
47
72
|
trackers.yaml
|
|
48
73
|
project-context.yaml
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
sprint-template.md
|
|
55
|
-
task-template.md
|
|
56
|
-
prompts/
|
|
57
|
-
clarifier.md
|
|
58
|
-
architect.md
|
|
59
|
-
verifier-designer.md
|
|
60
|
-
decomposer.md
|
|
61
|
-
reviewer-judge.md
|
|
62
|
-
mr-review-agent.md
|
|
63
|
-
agile-manager.md
|
|
64
|
-
publishing-agent.md
|
|
65
|
-
status-agent.md
|
|
66
|
-
scripts/
|
|
67
|
-
init.mjs
|
|
68
|
-
sync-status.mjs
|
|
69
|
-
verify-branch-strategy.mjs
|
|
70
|
-
task-branch-router.mjs
|
|
71
|
-
run-mr-review-gate.mjs
|
|
72
|
-
generate-prd-step.mjs
|
|
73
|
-
tracker/
|
|
74
|
-
local.mjs
|
|
75
|
-
github.mjs
|
|
76
|
-
mcp.mjs
|
|
77
|
-
custom.mjs
|
|
78
|
-
tracker-gateway.mjs
|
|
79
|
-
tasks/
|
|
80
|
-
backlog.md
|
|
81
|
-
sprint-1.md
|
|
82
|
-
task-status-log.md
|
|
74
|
+
node_modules/
|
|
75
|
+
@k0t0vich/meta-agents-template/
|
|
76
|
+
agents.md
|
|
77
|
+
tracker-command-template.md
|
|
78
|
+
template/.meta-agents/scripts/*.mjs
|
|
83
79
|
```
|
|
84
80
|
|
|
81
|
+
`agents.md` в проекте не перезаписывается целиком: `init` добавляет/обновляет в нём только ссылочный блок на правила из npm-пакета.
|
|
82
|
+
|
|
85
83
|
## Канонические команды
|
|
86
84
|
0. `VERIFY_GOVERNANCE_GATE`
|
|
87
85
|
1. `CREATE_TASK`
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import process from "node:process";
|
|
3
4
|
import readline from "node:readline/promises";
|
|
@@ -8,6 +9,25 @@ import { syncTemplate } from "./sync.mjs";
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
10
11
|
const templateRoot = path.resolve(__dirname, "..", "template");
|
|
12
|
+
const packageManifestPath = path.resolve(__dirname, "..", "package.json");
|
|
13
|
+
|
|
14
|
+
function readSelfPackageIdentity() {
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(packageManifestPath, "utf8");
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
return {
|
|
19
|
+
name: String(parsed.name || "@k0t0vich/meta-agents-template"),
|
|
20
|
+
version: String(parsed.version || ""),
|
|
21
|
+
};
|
|
22
|
+
} catch {
|
|
23
|
+
return {
|
|
24
|
+
name: "@k0t0vich/meta-agents-template",
|
|
25
|
+
version: "",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const selfPackage = readSelfPackageIdentity();
|
|
11
31
|
|
|
12
32
|
function parseOptions(args) {
|
|
13
33
|
const options = {
|
|
@@ -60,13 +80,14 @@ function printHelp() {
|
|
|
60
80
|
" meta-agents --help",
|
|
61
81
|
"",
|
|
62
82
|
"Commands:",
|
|
63
|
-
" init Initialize a project
|
|
83
|
+
" init Initialize a project (config-only in root, npm-linked rules)",
|
|
64
84
|
" sync Update managed template files in an existing project",
|
|
65
85
|
" upgrade Alias for sync",
|
|
66
86
|
"",
|
|
67
87
|
"Init dialog:",
|
|
68
88
|
" By default, 'init' asks about Git mode and tracker provider.",
|
|
69
89
|
" Use --yes to skip dialog and use defaults/flags.",
|
|
90
|
+
" --force overwrites existing .meta-agents/config files and updates legacy meta:* scripts.",
|
|
70
91
|
];
|
|
71
92
|
console.log(lines.join("\n"));
|
|
72
93
|
}
|
|
@@ -233,11 +254,38 @@ export async function main(argv) {
|
|
|
233
254
|
tracker,
|
|
234
255
|
force: options.force,
|
|
235
256
|
gitSetup,
|
|
257
|
+
packageName: selfPackage.name,
|
|
258
|
+
packageVersion: selfPackage.version,
|
|
236
259
|
});
|
|
237
260
|
|
|
238
261
|
console.log(`Initialized meta-agents project at: ${result.targetDir}`);
|
|
239
262
|
console.log(`Default tracker: ${tracker}`);
|
|
240
263
|
console.log(`Git mode: ${result.git.mode}`);
|
|
264
|
+
if (result.packageJson.created) {
|
|
265
|
+
console.log("Package manifest: created package.json");
|
|
266
|
+
}
|
|
267
|
+
if (result.packageJson.dependencyAdded) {
|
|
268
|
+
console.log(`Package dependency: added ${selfPackage.name} to devDependencies`);
|
|
269
|
+
}
|
|
270
|
+
if (result.packageJson.updatedScripts.length > 0) {
|
|
271
|
+
console.log(`Meta scripts updated: ${result.packageJson.updatedScripts.join(", ")}`);
|
|
272
|
+
}
|
|
273
|
+
if (result.runtimePackage.installedNow) {
|
|
274
|
+
console.log(`Runtime package installed: ${selfPackage.name}`);
|
|
275
|
+
}
|
|
276
|
+
if (result.config.updated.length > 0) {
|
|
277
|
+
console.log(`Config files written: ${result.config.updated.join(", ")}`);
|
|
278
|
+
}
|
|
279
|
+
if (result.config.skipped.length > 0) {
|
|
280
|
+
console.log(`Config files kept: ${result.config.skipped.join(", ")}`);
|
|
281
|
+
}
|
|
282
|
+
if (result.agents.created) {
|
|
283
|
+
console.log("agents.md: created with npm links to template rules");
|
|
284
|
+
} else if (result.agents.updated) {
|
|
285
|
+
console.log("agents.md: appended/updated npm links block");
|
|
286
|
+
} else {
|
|
287
|
+
console.log("agents.md: npm links block already up to date");
|
|
288
|
+
}
|
|
241
289
|
if (result.git.actions.length > 0) {
|
|
242
290
|
console.log(`Git actions: ${result.git.actions.join("; ")}`);
|
|
243
291
|
}
|
package/src/init.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
4
5
|
|
|
5
6
|
const TEXT_EXTENSIONS = new Set([
|
|
6
7
|
".md",
|
|
@@ -12,10 +13,25 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
12
13
|
".js",
|
|
13
14
|
]);
|
|
14
15
|
|
|
16
|
+
const AGENTS_LINK_BLOCK_START = "<!-- meta-agents-template:links:start -->";
|
|
17
|
+
const AGENTS_LINK_BLOCK_END = "<!-- meta-agents-template:links:end -->";
|
|
18
|
+
|
|
15
19
|
function resolveTargetDir(cwd, target) {
|
|
16
20
|
return path.resolve(cwd, target);
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
function escapeRegExp(value) {
|
|
24
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function replaceTemplatePlaceholders(input, values) {
|
|
28
|
+
let output = input;
|
|
29
|
+
for (const [key, value] of Object.entries(values)) {
|
|
30
|
+
output = output.replaceAll(`__${key}__`, value);
|
|
31
|
+
}
|
|
32
|
+
return output;
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
async function runCommand(command, args, cwd, allowFailure = false) {
|
|
20
36
|
return new Promise((resolve, reject) => {
|
|
21
37
|
const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -51,6 +67,15 @@ async function runCommand(command, args, cwd, allowFailure = false) {
|
|
|
51
67
|
});
|
|
52
68
|
}
|
|
53
69
|
|
|
70
|
+
async function pathExists(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
await fs.access(filePath);
|
|
73
|
+
return true;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
async function directoryExists(dir) {
|
|
55
80
|
try {
|
|
56
81
|
const stat = await fs.stat(dir);
|
|
@@ -60,11 +85,6 @@ async function directoryExists(dir) {
|
|
|
60
85
|
}
|
|
61
86
|
}
|
|
62
87
|
|
|
63
|
-
async function isDirectoryEmpty(dir) {
|
|
64
|
-
const entries = await fs.readdir(dir);
|
|
65
|
-
return entries.length === 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
88
|
async function walkFiles(dir, list = []) {
|
|
69
89
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
70
90
|
for (const entry of entries) {
|
|
@@ -82,25 +102,198 @@ function shouldReplacePlaceholders(filePath) {
|
|
|
82
102
|
return TEXT_EXTENSIONS.has(path.extname(filePath));
|
|
83
103
|
}
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
const
|
|
105
|
+
function buildMetaScripts(packageName) {
|
|
106
|
+
const base = `node ./node_modules/${packageName}/template/.meta-agents/scripts`;
|
|
107
|
+
return {
|
|
108
|
+
"meta:status": `${base}/sync-status.mjs`,
|
|
109
|
+
"meta:branch": `${base}/verify-branch-strategy.mjs`,
|
|
110
|
+
"meta:task-start": `${base}/task-branch-router.mjs`,
|
|
111
|
+
"meta:verify": `${base}/verify-governance.mjs`,
|
|
112
|
+
"meta:review": `${base}/run-review-gate.mjs`,
|
|
113
|
+
"meta:review-approve": `${base}/run-review-gate.mjs --approve yes`,
|
|
114
|
+
"meta:mr-review": `${base}/run-mr-review-gate.mjs`,
|
|
115
|
+
"meta:mr-review-approve": `${base}/run-mr-review-gate.mjs --approve yes`,
|
|
116
|
+
"meta:verify-link": `${base}/verify-commit-link.mjs`,
|
|
117
|
+
"meta:ops": `${base}/tracker-gateway.mjs`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isLegacyMetaScript(value) {
|
|
122
|
+
return (
|
|
123
|
+
value.includes(".meta-agents/scripts/") ||
|
|
124
|
+
value.includes("meta-agents-template/template/.meta-agents/scripts/")
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function defaultPackageNameFromDir(targetDir) {
|
|
129
|
+
const raw = path.basename(targetDir).toLowerCase();
|
|
130
|
+
const sanitized = raw.replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
131
|
+
return sanitized || "meta-agents-project";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function ensurePackageJson(targetDir) {
|
|
135
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
136
|
+
if (await pathExists(packageJsonPath)) {
|
|
137
|
+
const input = await fs.readFile(packageJsonPath, "utf8");
|
|
138
|
+
try {
|
|
139
|
+
return {
|
|
140
|
+
path: packageJsonPath,
|
|
141
|
+
data: JSON.parse(input),
|
|
142
|
+
created: false,
|
|
143
|
+
};
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw new Error(`invalid package.json at ${packageJsonPath}: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const data = {
|
|
150
|
+
name: defaultPackageNameFromDir(targetDir),
|
|
151
|
+
version: "0.1.0",
|
|
152
|
+
private: true,
|
|
153
|
+
scripts: {},
|
|
154
|
+
devDependencies: {},
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
path: packageJsonPath,
|
|
158
|
+
data,
|
|
159
|
+
created: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function mergePackageJsonMeta({
|
|
164
|
+
targetDir,
|
|
165
|
+
packageName,
|
|
166
|
+
packageVersion,
|
|
167
|
+
force,
|
|
168
|
+
}) {
|
|
169
|
+
const packageJson = await ensurePackageJson(targetDir);
|
|
170
|
+
const data = packageJson.data || {};
|
|
171
|
+
data.scripts = data.scripts && typeof data.scripts === "object" ? data.scripts : {};
|
|
172
|
+
data.devDependencies =
|
|
173
|
+
data.devDependencies && typeof data.devDependencies === "object" ? data.devDependencies : {};
|
|
174
|
+
|
|
175
|
+
const desiredScripts = buildMetaScripts(packageName);
|
|
176
|
+
const updatedScripts = [];
|
|
177
|
+
const skippedScripts = [];
|
|
87
178
|
|
|
88
|
-
for (const
|
|
89
|
-
|
|
179
|
+
for (const [key, value] of Object.entries(desiredScripts)) {
|
|
180
|
+
const current = data.scripts[key];
|
|
181
|
+
if (!current) {
|
|
182
|
+
data.scripts[key] = value;
|
|
183
|
+
updatedScripts.push(key);
|
|
90
184
|
continue;
|
|
91
185
|
}
|
|
92
186
|
|
|
93
|
-
|
|
94
|
-
|
|
187
|
+
if (current !== value && (force || isLegacyMetaScript(current))) {
|
|
188
|
+
data.scripts[key] = value;
|
|
189
|
+
updatedScripts.push(key);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
skippedScripts.push(key);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let dependencyAdded = false;
|
|
197
|
+
if (!data.devDependencies[packageName]) {
|
|
198
|
+
const spec = packageVersion ? `^${packageVersion}` : "latest";
|
|
199
|
+
data.devDependencies[packageName] = spec;
|
|
200
|
+
dependencyAdded = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (packageJson.created || updatedScripts.length > 0 || dependencyAdded) {
|
|
204
|
+
await fs.writeFile(packageJson.path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
205
|
+
}
|
|
95
206
|
|
|
96
|
-
|
|
97
|
-
|
|
207
|
+
return {
|
|
208
|
+
created: packageJson.created,
|
|
209
|
+
updatedScripts,
|
|
210
|
+
skippedScripts,
|
|
211
|
+
dependencyAdded,
|
|
212
|
+
packageJsonPath: packageJson.path,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function buildAgentsLinkBlock(packageName) {
|
|
217
|
+
const packageRoot = `./node_modules/${packageName}`;
|
|
218
|
+
const lines = [
|
|
219
|
+
AGENTS_LINK_BLOCK_START,
|
|
220
|
+
"## Meta Agents Template (npm-linked)",
|
|
221
|
+
"Базовые правила шаблона:",
|
|
222
|
+
`- [Template agents.md](${packageRoot}/agents.md)`,
|
|
223
|
+
`- [Template tracker-command-template.md](${packageRoot}/tracker-command-template.md)`,
|
|
224
|
+
"Локальные конфиги проекта:",
|
|
225
|
+
"- [.meta-agents/config/project-context.yaml](./.meta-agents/config/project-context.yaml)",
|
|
226
|
+
"- [.meta-agents/config/trackers.yaml](./.meta-agents/config/trackers.yaml)",
|
|
227
|
+
"- [.meta-agents/config/system.yaml](./.meta-agents/config/system.yaml)",
|
|
228
|
+
AGENTS_LINK_BLOCK_END,
|
|
229
|
+
];
|
|
230
|
+
return `${lines.join("\n")}\n`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function upsertAgentsLinks(targetDir, packageName) {
|
|
234
|
+
const agentsPath = path.join(targetDir, "agents.md");
|
|
235
|
+
const block = buildAgentsLinkBlock(packageName);
|
|
236
|
+
|
|
237
|
+
if (!(await pathExists(agentsPath))) {
|
|
238
|
+
const content = ["# agents.md", "", block].join("\n");
|
|
239
|
+
await fs.writeFile(agentsPath, content, "utf8");
|
|
240
|
+
return { created: true, updated: false, path: agentsPath };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const input = await fs.readFile(agentsPath, "utf8");
|
|
244
|
+
const blockRegex = new RegExp(
|
|
245
|
+
`${escapeRegExp(AGENTS_LINK_BLOCK_START)}[\\s\\S]*?${escapeRegExp(AGENTS_LINK_BLOCK_END)}\\n?`,
|
|
246
|
+
"m",
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
let output = input;
|
|
250
|
+
let updated = false;
|
|
251
|
+
|
|
252
|
+
if (blockRegex.test(input)) {
|
|
253
|
+
output = input.replace(blockRegex, block);
|
|
254
|
+
updated = output !== input;
|
|
255
|
+
} else {
|
|
256
|
+
const glue = input.endsWith("\n") ? "\n" : "\n\n";
|
|
257
|
+
output = `${input}${glue}${block}`;
|
|
258
|
+
updated = true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (updated) {
|
|
262
|
+
await fs.writeFile(agentsPath, output, "utf8");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return { created: false, updated, path: agentsPath };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function syncConfigFiles({ templateRoot, targetDir, values, force }) {
|
|
269
|
+
const sourceConfigRoot = path.join(templateRoot, ".meta-agents", "config");
|
|
270
|
+
const targetConfigRoot = path.join(targetDir, ".meta-agents", "config");
|
|
271
|
+
const sourceFiles = await walkFiles(sourceConfigRoot);
|
|
272
|
+
|
|
273
|
+
const updated = [];
|
|
274
|
+
const skipped = [];
|
|
275
|
+
|
|
276
|
+
for (const sourceFile of sourceFiles) {
|
|
277
|
+
const relative = path.relative(sourceConfigRoot, sourceFile);
|
|
278
|
+
const destination = path.join(targetConfigRoot, relative);
|
|
279
|
+
|
|
280
|
+
if (!force && (await pathExists(destination))) {
|
|
281
|
+
skipped.push(path.join(".meta-agents", "config", relative));
|
|
282
|
+
continue;
|
|
98
283
|
}
|
|
99
284
|
|
|
100
|
-
|
|
101
|
-
|
|
285
|
+
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
286
|
+
if (shouldReplacePlaceholders(sourceFile)) {
|
|
287
|
+
const raw = await fs.readFile(sourceFile, "utf8");
|
|
288
|
+
const rendered = replaceTemplatePlaceholders(raw, values);
|
|
289
|
+
await fs.writeFile(destination, rendered, "utf8");
|
|
290
|
+
} else {
|
|
291
|
+
await fs.copyFile(sourceFile, destination);
|
|
102
292
|
}
|
|
293
|
+
updated.push(path.join(".meta-agents", "config", relative));
|
|
103
294
|
}
|
|
295
|
+
|
|
296
|
+
return { updated, skipped };
|
|
104
297
|
}
|
|
105
298
|
|
|
106
299
|
async function setupGit(targetDir, gitSetup) {
|
|
@@ -167,31 +360,70 @@ async function setupGit(targetDir, gitSetup) {
|
|
|
167
360
|
return { mode: setup.mode, actions, warnings };
|
|
168
361
|
}
|
|
169
362
|
|
|
170
|
-
|
|
363
|
+
function packageDirFromName(targetDir, packageName) {
|
|
364
|
+
return path.join(targetDir, "node_modules", ...packageName.split("/"));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function ensureRuntimePackageInstalled({ targetDir, packageName, packageVersion }) {
|
|
368
|
+
const installDir = packageDirFromName(targetDir, packageName);
|
|
369
|
+
if (await directoryExists(installDir)) {
|
|
370
|
+
return { installedNow: false, installDir };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const spec = packageVersion ? `${packageName}@${packageVersion}` : packageName;
|
|
374
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
375
|
+
await runCommand(npmCommand, ["install", "-D", spec], targetDir);
|
|
376
|
+
return { installedNow: true, installDir };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export async function initProject({
|
|
380
|
+
cwd,
|
|
381
|
+
templateRoot,
|
|
382
|
+
target,
|
|
383
|
+
tracker,
|
|
384
|
+
force,
|
|
385
|
+
gitSetup,
|
|
386
|
+
packageName = "@k0t0vich/meta-agents-template",
|
|
387
|
+
packageVersion = "",
|
|
388
|
+
}) {
|
|
171
389
|
const targetDir = resolveTargetDir(cwd, target);
|
|
172
390
|
const exists = await directoryExists(targetDir);
|
|
173
|
-
|
|
174
|
-
if (exists) {
|
|
175
|
-
const empty = await isDirectoryEmpty(targetDir);
|
|
176
|
-
if (!empty && !force) {
|
|
177
|
-
throw new Error(`target directory is not empty: ${targetDir}. Use --force to overwrite.`);
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
391
|
+
if (!exists) {
|
|
180
392
|
await fs.mkdir(targetDir, { recursive: true });
|
|
181
393
|
}
|
|
182
394
|
|
|
183
|
-
await
|
|
184
|
-
|
|
395
|
+
const packageJson = await mergePackageJsonMeta({
|
|
396
|
+
targetDir,
|
|
397
|
+
packageName,
|
|
398
|
+
packageVersion,
|
|
185
399
|
force,
|
|
186
|
-
|
|
400
|
+
});
|
|
401
|
+
const runtimePackage = await ensureRuntimePackageInstalled({
|
|
402
|
+
targetDir,
|
|
403
|
+
packageName,
|
|
404
|
+
packageVersion,
|
|
187
405
|
});
|
|
188
406
|
|
|
189
407
|
const projectName = path.basename(targetDir);
|
|
190
|
-
await
|
|
191
|
-
|
|
192
|
-
|
|
408
|
+
const config = await syncConfigFiles({
|
|
409
|
+
templateRoot,
|
|
410
|
+
targetDir,
|
|
411
|
+
values: {
|
|
412
|
+
PROJECT_NAME: projectName,
|
|
413
|
+
DEFAULT_TRACKER: tracker,
|
|
414
|
+
},
|
|
415
|
+
force,
|
|
193
416
|
});
|
|
194
417
|
|
|
418
|
+
const agents = await upsertAgentsLinks(targetDir, packageName);
|
|
195
419
|
const git = await setupGit(targetDir, gitSetup);
|
|
196
|
-
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
targetDir,
|
|
423
|
+
git,
|
|
424
|
+
packageJson,
|
|
425
|
+
runtimePackage,
|
|
426
|
+
config,
|
|
427
|
+
agents,
|
|
428
|
+
};
|
|
197
429
|
}
|