@k0t0vich/meta-agents-template 0.1.6 → 0.1.8

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 CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to this package are documented in this file.
4
4
 
5
+ ## 0.1.8 - 2026-03-20
6
+
7
+ ### Changed
8
+ - Finalized npm-linked rules model: generated projects keep root configs only (`project-context.yaml`, `trackers.yaml`) and execute meta scripts directly from `node_modules/@k0t0vich/meta-agents-template/template/.meta-agents/scripts/*`.
9
+ - Reworked root bootstrap docs and `agents.md` so project context stays local while operational rules are consumed from template-linked sources.
10
+ - Restored reliable status flow for both source repo and generated projects (`meta:status` fallback paths documented and runnable).
11
+ - Tightened commit-message validation to canonical format `#<issue-number> <summary>` and synchronized policy text with that format.
12
+ - Expanded local release smoke to validate two required scenarios: linked execution from current code and installation flow from an empty folder.
13
+
14
+ ## 0.1.7 - 2026-03-20
15
+
16
+ ### Changed
17
+ - 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`.
18
+ - Prevented accidental overwrite of existing project `agents.md`: init now appends/updates only a managed links block instead of replacing full content.
19
+ - Reworked package setup in existing projects: `package.json` is merged (not replaced), preserving existing scripts while adding `meta:*` commands and `devDependencies` entry.
20
+ - Added runtime package self-install during init when package files are not present locally (safe `npx` path).
21
+ - Updated public README with robust one-line onboarding for npm URL/package-name prompts and new post-init layout.
22
+ - Updated local post-publish smoke to validate the new config-only root flow.
23
+
5
24
  ## 0.1.6 - 2026-03-20
6
25
 
7
26
  ### 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 my-project
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: в корне создаёт/обновляет только project-specific конфиги `.meta-agents/config/project-context.yaml` (имя проекта + tracker) и `.meta-agents/config/trackers.yaml`, аккуратно merge-ит `meta:*` scripts в `package.json` и добавляет ссылочный блок в `agents.md` вместо полной перезаписи.
35
+ - `--force` нужен только если надо перезаписать уже существующие project-specific конфиги (`project-context.yaml`, `trackers.yaml`) и мигрировать 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
- npm i -D git+https://github.com/k0t0vich/meta-agents-template.git
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/template/agents.md && echo "meta-agents template ready"
23
52
  ```
24
53
 
25
54
  ## Диалог при `init`
@@ -33,55 +62,22 @@ 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
- README.md
66
+ package-lock.json
41
67
  agents.md
42
- tracker-command-template.md
43
68
  .meta-agents/
44
69
  config/
45
- system.yaml
46
- roles.yaml
47
- trackers.yaml
48
70
  project-context.yaml
49
- templates/
50
- prd-step.md
51
- agent-work-contract.md
52
- verification-plan.md
53
- evidence-pack.md
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
71
+ trackers.yaml
72
+ node_modules/
73
+ @k0t0vich/meta-agents-template/
74
+ template/agents.md
75
+ template/tracker-command-template.md
76
+ template/.meta-agents/scripts/*.mjs
83
77
  ```
84
78
 
79
+ `agents.md` в проекте не перезаписывается целиком: `init` добавляет/обновляет в нём только ссылочный блок на правила из npm-пакета.
80
+
85
81
  ## Канонические команды
86
82
  0. `VERIFY_GOVERNANCE_GATE`
87
83
  1. `CREATE_TASK`
@@ -133,7 +129,7 @@ npm run self:bootstrap
133
129
  10. Есть отдельное явное подтверждение пользователя на прохождение review (`Review Approved: yes`).
134
130
  11. Перед merge пройден MR review gate (`RUN_MR_REVIEW_GATE`) с финальным `PASS_CONFIRMED`.
135
131
  12. Есть отдельное явное подтверждение пользователя на MR review (`MR Review Approved: yes`).
136
- 13. Каждый коммит связан с issue (`#issue` в commit message).
132
+ 13. Каждый коммит следует формату `#issue-number <summary>` (issue ref в начале commit message).
137
133
 
138
134
  Если хотя бы один пункт не выполнен, задача не принимается.
139
135
 
@@ -182,7 +178,7 @@ npm run meta:status
182
178
  - для большой фичи (в режиме GitHub tracker) агент обязан предложить `feature issue` + `epic issue` и отдельную ветку.
183
179
 
184
180
  ## Основные документы шаблона
185
- - [agents.md](./agents.md)
186
- - [tracker-command-template.md](./tracker-command-template.md)
181
+ - [template/agents.md](./template/agents.md)
182
+ - [template/tracker-command-template.md](./template/tracker-command-template.md)
187
183
  - [CHANGELOG.md](./CHANGELOG.md)
188
184
  - [GitHub Releases](https://github.com/k0t0vich/meta-agents-template/releases)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k0t0vich/meta-agents-template",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Template system for verification-first agentic development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,13 +10,21 @@
10
10
  "start": "node bin/meta-agents.js --help",
11
11
  "self:sync": "node bin/meta-agents.js sync . --force",
12
12
  "self:bootstrap": "npm run self:sync && node scripts/self-bootstrap.mjs",
13
- "release:smoke-local": "node scripts/post-publish-local-smoke.mjs"
13
+ "release:smoke-local": "node scripts/post-publish-local-smoke.mjs",
14
+ "meta:status": "node template/.meta-agents/scripts/sync-status.mjs",
15
+ "meta:branch": "node template/.meta-agents/scripts/verify-branch-strategy.mjs",
16
+ "meta:task-start": "node template/.meta-agents/scripts/task-branch-router.mjs",
17
+ "meta:verify": "node template/.meta-agents/scripts/verify-governance.mjs",
18
+ "meta:review": "node template/.meta-agents/scripts/run-review-gate.mjs",
19
+ "meta:review-approve": "node template/.meta-agents/scripts/run-review-gate.mjs --approve yes",
20
+ "meta:mr-review": "node template/.meta-agents/scripts/run-mr-review-gate.mjs",
21
+ "meta:mr-review-approve": "node template/.meta-agents/scripts/run-mr-review-gate.mjs --approve yes",
22
+ "meta:verify-link": "node template/.meta-agents/scripts/verify-commit-link.mjs",
23
+ "meta:ops": "node template/.meta-agents/scripts/tracker-gateway.mjs"
14
24
  },
15
25
  "files": [
16
26
  "README.md",
17
27
  "CHANGELOG.md",
18
- "agents.md",
19
- "tracker-command-template.md",
20
28
  "bin",
21
29
  "src",
22
30
  "template"
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 from template",
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",
@@ -11,11 +12,27 @@ const TEXT_EXTENSIONS = new Set([
11
12
  ".mjs",
12
13
  ".js",
13
14
  ]);
15
+ const ROOT_CONFIG_FILES = ["project-context.yaml", "trackers.yaml"];
16
+
17
+ const AGENTS_LINK_BLOCK_START = "<!-- meta-agents-template:links:start -->";
18
+ const AGENTS_LINK_BLOCK_END = "<!-- meta-agents-template:links:end -->";
14
19
 
15
20
  function resolveTargetDir(cwd, target) {
16
21
  return path.resolve(cwd, target);
17
22
  }
18
23
 
24
+ function escapeRegExp(value) {
25
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26
+ }
27
+
28
+ function replaceTemplatePlaceholders(input, values) {
29
+ let output = input;
30
+ for (const [key, value] of Object.entries(values)) {
31
+ output = output.replaceAll(`__${key}__`, value);
32
+ }
33
+ return output;
34
+ }
35
+
19
36
  async function runCommand(command, args, cwd, allowFailure = false) {
20
37
  return new Promise((resolve, reject) => {
21
38
  const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -51,6 +68,15 @@ async function runCommand(command, args, cwd, allowFailure = false) {
51
68
  });
52
69
  }
53
70
 
71
+ async function pathExists(filePath) {
72
+ try {
73
+ await fs.access(filePath);
74
+ return true;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
54
80
  async function directoryExists(dir) {
55
81
  try {
56
82
  const stat = await fs.stat(dir);
@@ -60,11 +86,6 @@ async function directoryExists(dir) {
60
86
  }
61
87
  }
62
88
 
63
- async function isDirectoryEmpty(dir) {
64
- const entries = await fs.readdir(dir);
65
- return entries.length === 0;
66
- }
67
-
68
89
  async function walkFiles(dir, list = []) {
69
90
  const entries = await fs.readdir(dir, { withFileTypes: true });
70
91
  for (const entry of entries) {
@@ -82,25 +103,197 @@ function shouldReplacePlaceholders(filePath) {
82
103
  return TEXT_EXTENSIONS.has(path.extname(filePath));
83
104
  }
84
105
 
85
- async function replacePlaceholders(rootDir, values) {
86
- const files = await walkFiles(rootDir);
106
+ function buildMetaScripts(packageName) {
107
+ const base = `node ./node_modules/${packageName}/template/.meta-agents/scripts`;
108
+ return {
109
+ "meta:status": `${base}/sync-status.mjs`,
110
+ "meta:branch": `${base}/verify-branch-strategy.mjs`,
111
+ "meta:task-start": `${base}/task-branch-router.mjs`,
112
+ "meta:verify": `${base}/verify-governance.mjs`,
113
+ "meta:review": `${base}/run-review-gate.mjs`,
114
+ "meta:review-approve": `${base}/run-review-gate.mjs --approve yes`,
115
+ "meta:mr-review": `${base}/run-mr-review-gate.mjs`,
116
+ "meta:mr-review-approve": `${base}/run-mr-review-gate.mjs --approve yes`,
117
+ "meta:verify-link": `${base}/verify-commit-link.mjs`,
118
+ "meta:ops": `${base}/tracker-gateway.mjs`,
119
+ };
120
+ }
121
+
122
+ function isLegacyMetaScript(value) {
123
+ return (
124
+ value.includes(".meta-agents/scripts/") ||
125
+ value.includes("meta-agents-template/template/.meta-agents/scripts/")
126
+ );
127
+ }
128
+
129
+ function defaultPackageNameFromDir(targetDir) {
130
+ const raw = path.basename(targetDir).toLowerCase();
131
+ const sanitized = raw.replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
132
+ return sanitized || "meta-agents-project";
133
+ }
134
+
135
+ async function ensurePackageJson(targetDir) {
136
+ const packageJsonPath = path.join(targetDir, "package.json");
137
+ if (await pathExists(packageJsonPath)) {
138
+ const input = await fs.readFile(packageJsonPath, "utf8");
139
+ try {
140
+ return {
141
+ path: packageJsonPath,
142
+ data: JSON.parse(input),
143
+ created: false,
144
+ };
145
+ } catch (error) {
146
+ throw new Error(`invalid package.json at ${packageJsonPath}: ${error.message}`);
147
+ }
148
+ }
149
+
150
+ const data = {
151
+ name: defaultPackageNameFromDir(targetDir),
152
+ version: "0.1.0",
153
+ private: true,
154
+ scripts: {},
155
+ devDependencies: {},
156
+ };
157
+ return {
158
+ path: packageJsonPath,
159
+ data,
160
+ created: true,
161
+ };
162
+ }
163
+
164
+ async function mergePackageJsonMeta({
165
+ targetDir,
166
+ packageName,
167
+ packageVersion,
168
+ force,
169
+ }) {
170
+ const packageJson = await ensurePackageJson(targetDir);
171
+ const data = packageJson.data || {};
172
+ data.scripts = data.scripts && typeof data.scripts === "object" ? data.scripts : {};
173
+ data.devDependencies =
174
+ data.devDependencies && typeof data.devDependencies === "object" ? data.devDependencies : {};
175
+
176
+ const desiredScripts = buildMetaScripts(packageName);
177
+ const updatedScripts = [];
178
+ const skippedScripts = [];
87
179
 
88
- for (const file of files) {
89
- if (!shouldReplacePlaceholders(file)) {
180
+ for (const [key, value] of Object.entries(desiredScripts)) {
181
+ const current = data.scripts[key];
182
+ if (!current) {
183
+ data.scripts[key] = value;
184
+ updatedScripts.push(key);
90
185
  continue;
91
186
  }
92
187
 
93
- const input = await fs.readFile(file, "utf8");
94
- let output = input;
188
+ if (current !== value && (force || isLegacyMetaScript(current))) {
189
+ data.scripts[key] = value;
190
+ updatedScripts.push(key);
191
+ continue;
192
+ }
193
+
194
+ skippedScripts.push(key);
195
+ }
196
+
197
+ let dependencyAdded = false;
198
+ if (!data.devDependencies[packageName]) {
199
+ const spec = packageVersion ? `^${packageVersion}` : "latest";
200
+ data.devDependencies[packageName] = spec;
201
+ dependencyAdded = true;
202
+ }
203
+
204
+ if (packageJson.created || updatedScripts.length > 0 || dependencyAdded) {
205
+ await fs.writeFile(packageJson.path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
206
+ }
95
207
 
96
- for (const [key, value] of Object.entries(values)) {
97
- output = output.replaceAll(`__${key}__`, value);
208
+ return {
209
+ created: packageJson.created,
210
+ updatedScripts,
211
+ skippedScripts,
212
+ dependencyAdded,
213
+ packageJsonPath: packageJson.path,
214
+ };
215
+ }
216
+
217
+ function buildAgentsLinkBlock(packageName) {
218
+ const packageRoot = `./node_modules/${packageName}`;
219
+ const lines = [
220
+ AGENTS_LINK_BLOCK_START,
221
+ "## Meta Agents Template (npm-linked)",
222
+ "Базовые правила шаблона:",
223
+ `- [Template agents.md](${packageRoot}/template/agents.md)`,
224
+ `- [Template tracker-command-template.md](${packageRoot}/template/tracker-command-template.md)`,
225
+ "Локальные конфиги проекта:",
226
+ "- [.meta-agents/config/project-context.yaml](./.meta-agents/config/project-context.yaml)",
227
+ "- [.meta-agents/config/trackers.yaml](./.meta-agents/config/trackers.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 = ROOT_CONFIG_FILES.map((file) => path.join(sourceConfigRoot, file));
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
- if (output !== input) {
101
- await fs.writeFile(file, output, "utf8");
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
- export async function initProject({ cwd, templateRoot, target, tracker, force, gitSetup }) {
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 fs.cp(templateRoot, targetDir, {
184
- recursive: true,
395
+ const packageJson = await mergePackageJsonMeta({
396
+ targetDir,
397
+ packageName,
398
+ packageVersion,
185
399
  force,
186
- errorOnExist: !force,
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 replacePlaceholders(targetDir, {
191
- PROJECT_NAME: projectName,
192
- DEFAULT_TRACKER: tracker,
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
- return { targetDir, git };
420
+
421
+ return {
422
+ targetDir,
423
+ git,
424
+ packageJson,
425
+ runtimePackage,
426
+ config,
427
+ agents,
428
+ };
197
429
  }
@@ -1,4 +1,6 @@
1
1
  project_context:
2
+ project:
3
+ name: "__PROJECT_NAME__"
2
4
  tracker_provider:
3
5
  selected: "__DEFAULT_TRACKER__"
4
6
  locked: true
@@ -208,7 +208,7 @@ system:
208
208
  commit_linkage:
209
209
  required: true
210
210
  accepted_patterns:
211
- - "#issue"
211
+ - "#<issue-number> <summary>"
212
212
  publish_flow:
213
213
  changelog_required: true
214
214
  release_note_required: true
@@ -22,6 +22,9 @@ Tooling:
22
22
  - Основной путь: `npm run meta:status`.
23
23
  - Для machine-readable вывода можно использовать `npm run meta:status -- --json`.
24
24
  - Для веточного preflight по конкретной задаче: `npm run meta:task-start -- --task <#issue> --slug <slug>`.
25
+ - Fallback (если `meta:status` отсутствует в npm scripts):
26
+ - source repo: `node template/.meta-agents/scripts/sync-status.mjs --json`
27
+ - generated project: `node ./node_modules/@k0t0vich/meta-agents-template/template/.meta-agents/scripts/sync-status.mjs --json`
25
28
 
26
29
  Rule:
27
30
  - В `github` режиме локальные `tasks/*` не считать авторитетным источником по умолчанию; рассматривать только как cache/legacy artifacts.