@k0t0vich/meta-agents-template 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +52 -10
  3. package/agents.md +67 -16
  4. package/package.json +1 -1
  5. package/template/.github/workflows/gitflow-lite-verify.yml +63 -0
  6. package/template/.meta-agents/config/project-context.yaml +7 -0
  7. package/template/.meta-agents/config/roles.yaml +5 -1
  8. package/template/.meta-agents/config/system.yaml +125 -3
  9. package/template/.meta-agents/config/trackers.yaml +1 -0
  10. package/template/.meta-agents/prompts/agile-manager.md +19 -1
  11. package/template/.meta-agents/prompts/clarifier.md +2 -0
  12. package/template/.meta-agents/prompts/mr-review-agent.md +26 -0
  13. package/template/.meta-agents/prompts/reviewer-judge.md +3 -1
  14. package/template/.meta-agents/prompts/status-agent.md +27 -0
  15. package/template/.meta-agents/scripts/run-mr-review-gate.mjs +488 -0
  16. package/template/.meta-agents/scripts/run-review-gate.mjs +54 -1
  17. package/template/.meta-agents/scripts/sync-status.mjs +620 -1
  18. package/template/.meta-agents/scripts/task-branch-router.mjs +530 -0
  19. package/template/.meta-agents/scripts/tracker/provider-lock.mjs +104 -0
  20. package/template/.meta-agents/scripts/tracker-gateway.mjs +199 -0
  21. package/template/.meta-agents/scripts/verify-branch-strategy.mjs +103 -0
  22. package/template/.meta-agents/scripts/verify-commit-link.mjs +27 -13
  23. package/template/.meta-agents/scripts/verify-governance.mjs +37 -12
  24. package/template/.meta-agents/templates/agent-work-contract.md +8 -1
  25. package/template/.meta-agents/templates/task-template.md +7 -1
  26. package/template/README.md +43 -6
  27. package/template/agents.md +67 -16
  28. package/template/package.json +6 -1
  29. package/template/tracker-command-template.md +122 -28
  30. package/tracker-command-template.md +109 -15
@@ -0,0 +1,199 @@
1
+ import process from "node:process";
2
+ import { execFileSync } from "node:child_process";
3
+ import { resolveTrackerForCommand } from "./tracker/provider-lock.mjs";
4
+
5
+ const COMMAND_MAP = {
6
+ CREATE_TASK: "createTask",
7
+ PREPARE_TASK_BRANCH: "__prepareTaskBranch",
8
+ RUN_MR_REVIEW_GATE: "__runMrReviewGate",
9
+ SET_STATUS: "setStatus",
10
+ COMMIT_BY_NAME: "commitByName",
11
+ ASSIGN_SPRINT: "assignSprint",
12
+ };
13
+
14
+ function printHelp() {
15
+ console.log("TrackerGateway CLI");
16
+ console.log("");
17
+ console.log("Usage:");
18
+ console.log(
19
+ " npm run meta:ops -- --command CREATE_TASK|PREPARE_TASK_BRANCH|RUN_MR_REVIEW_GATE|SET_STATUS|COMMIT_BY_NAME|ASSIGN_SPRINT [--tracker github|mcp|local|custom] [--payload '{\"k\":\"v\"}']",
20
+ );
21
+ console.log("");
22
+ console.log("Rules:");
23
+ console.log(" - If --tracker is passed, it must match locked project tracker provider.");
24
+ console.log(
25
+ " - Locked provider is resolved from .meta-agents/config/project-context.yaml and trackers.yaml.",
26
+ );
27
+ }
28
+
29
+ function runTaskBranchRouter(payload) {
30
+ const args = [".meta-agents/scripts/task-branch-router.mjs"];
31
+ if (payload.task) {
32
+ args.push("--task", String(payload.task));
33
+ }
34
+ if (payload.slug) {
35
+ args.push("--slug", String(payload.slug));
36
+ }
37
+ if (payload.kind) {
38
+ args.push("--kind", String(payload.kind));
39
+ }
40
+ if (payload.base) {
41
+ args.push("--base", String(payload.base));
42
+ }
43
+ if (payload.apply) {
44
+ args.push("--apply");
45
+ }
46
+ if (payload.allowDirty) {
47
+ args.push("--allow-dirty");
48
+ }
49
+ if (payload.allowAhead) {
50
+ args.push("--allow-ahead");
51
+ }
52
+ if (payload.json) {
53
+ args.push("--json");
54
+ }
55
+
56
+ const output = execFileSync("node", args, {
57
+ cwd: process.cwd(),
58
+ encoding: "utf8",
59
+ stdio: ["ignore", "pipe", "pipe"],
60
+ });
61
+ if (output.trim()) {
62
+ console.log(output.trim());
63
+ }
64
+ }
65
+
66
+ function runMrReviewGate(payload) {
67
+ const args = [".meta-agents/scripts/run-mr-review-gate.mjs"];
68
+ if (payload.pr) {
69
+ args.push("--pr", String(payload.pr));
70
+ }
71
+ if (payload.base) {
72
+ args.push("--base", String(payload.base));
73
+ }
74
+ if (payload.approve) {
75
+ args.push("--approve", "yes");
76
+ }
77
+ if (payload.json) {
78
+ args.push("--json");
79
+ }
80
+
81
+ const output = execFileSync("node", args, {
82
+ cwd: process.cwd(),
83
+ encoding: "utf8",
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ });
86
+ if (output.trim()) {
87
+ console.log(output.trim());
88
+ }
89
+ }
90
+
91
+ function parseArgs(argv) {
92
+ const options = {
93
+ command: "",
94
+ tracker: "",
95
+ payloadRaw: "",
96
+ help: false,
97
+ };
98
+
99
+ for (let i = 0; i < argv.length; i += 1) {
100
+ const arg = argv[i];
101
+ if (arg === "--help" || arg === "-h") {
102
+ options.help = true;
103
+ continue;
104
+ }
105
+ if (arg === "--command") {
106
+ options.command = (argv[i + 1] || "").trim().toUpperCase();
107
+ i += 1;
108
+ continue;
109
+ }
110
+ if (arg === "--tracker") {
111
+ options.tracker = (argv[i + 1] || "").trim().toLowerCase();
112
+ i += 1;
113
+ continue;
114
+ }
115
+ if (arg === "--payload") {
116
+ options.payloadRaw = argv[i + 1] || "";
117
+ i += 1;
118
+ continue;
119
+ }
120
+ }
121
+
122
+ return options;
123
+ }
124
+
125
+ function parsePayload(payloadRaw) {
126
+ if (!payloadRaw) {
127
+ return {};
128
+ }
129
+ try {
130
+ const payload = JSON.parse(payloadRaw);
131
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
132
+ throw new Error("payload must be a JSON object");
133
+ }
134
+ return payload;
135
+ } catch (error) {
136
+ throw new Error(`invalid --payload JSON: ${error.message}`);
137
+ }
138
+ }
139
+
140
+ async function loadAdapter(provider) {
141
+ try {
142
+ return await import(`./tracker/${provider}.mjs`);
143
+ } catch (error) {
144
+ throw new Error(`failed to load tracker adapter '${provider}': ${error.message}`);
145
+ }
146
+ }
147
+
148
+ async function main() {
149
+ const options = parseArgs(process.argv.slice(2));
150
+ if (options.help) {
151
+ printHelp();
152
+ return;
153
+ }
154
+
155
+ if (!options.command) {
156
+ throw new Error("missing --command");
157
+ }
158
+
159
+ const adapterMethod = COMMAND_MAP[options.command];
160
+ if (!adapterMethod) {
161
+ throw new Error(
162
+ `unsupported --command '${options.command}'. Allowed: ${Object.keys(COMMAND_MAP).join(", ")}`,
163
+ );
164
+ }
165
+
166
+ const payload = parsePayload(options.payloadRaw);
167
+ const trackerProvider = resolveTrackerForCommand({
168
+ requestedTracker: options.tracker,
169
+ cwd: process.cwd(),
170
+ });
171
+
172
+ if (adapterMethod === "__prepareTaskBranch") {
173
+ runTaskBranchRouter(payload);
174
+ console.log(`TrackerGateway PASS: ${options.command} via tracker '${trackerProvider}'.`);
175
+ return;
176
+ }
177
+ if (adapterMethod === "__runMrReviewGate") {
178
+ runMrReviewGate(payload);
179
+ console.log(`TrackerGateway PASS: ${options.command} via tracker '${trackerProvider}'.`);
180
+ return;
181
+ }
182
+
183
+ const adapter = await loadAdapter(trackerProvider);
184
+ const operation = adapter[adapterMethod];
185
+
186
+ if (typeof operation !== "function") {
187
+ throw new Error(
188
+ `tracker adapter '${trackerProvider}' does not export '${adapterMethod}' operation`,
189
+ );
190
+ }
191
+
192
+ await operation(payload);
193
+ console.log(`TrackerGateway PASS: ${options.command} via tracker '${trackerProvider}'.`);
194
+ }
195
+
196
+ main().catch((error) => {
197
+ console.error(`TrackerGateway FAIL: ${error.message}`);
198
+ process.exit(1);
199
+ });
@@ -0,0 +1,103 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import process from "node:process";
3
+
4
+ const BRANCH_PATTERNS = [
5
+ { type: "main", regex: /^main$/ },
6
+ { type: "develop", regex: /^develop$/ },
7
+ { type: "feature", regex: /^feature\/.+/ },
8
+ { type: "release", regex: /^release\/.+/ },
9
+ { type: "hotfix", regex: /^hotfix\/.+/ },
10
+ { type: "feature", regex: /^codex\/feature\/.+/ },
11
+ { type: "release", regex: /^codex\/release\/.+/ },
12
+ { type: "hotfix", regex: /^codex\/hotfix\/.+/ },
13
+ ];
14
+
15
+ function git(args, allowFailure = false) {
16
+ try {
17
+ return execFileSync("git", args, {
18
+ encoding: "utf8",
19
+ stdio: ["ignore", "pipe", "pipe"],
20
+ }).trim();
21
+ } catch (error) {
22
+ if (allowFailure) {
23
+ return "";
24
+ }
25
+ throw error;
26
+ }
27
+ }
28
+
29
+ function detectBranch() {
30
+ const direct = git(["rev-parse", "--abbrev-ref", "HEAD"], true);
31
+ if (direct && direct !== "HEAD") {
32
+ return direct;
33
+ }
34
+
35
+ const status = git(["status", "--short", "--branch"], true);
36
+ const header = status.split("\n")[0] || "";
37
+
38
+ const noCommitsMatch = header.match(/^##\s+No commits yet on\s+(.+)$/);
39
+ if (noCommitsMatch) {
40
+ return noCommitsMatch[1].trim();
41
+ }
42
+
43
+ const branchMatch = header.match(/^##\s+([^\.\s]+)(?:\.\.\.[^\s]+)?/);
44
+ if (branchMatch) {
45
+ return branchMatch[1].trim();
46
+ }
47
+
48
+ return "";
49
+ }
50
+
51
+ function parseArgs(argv) {
52
+ const options = {
53
+ noMain: false,
54
+ };
55
+
56
+ for (let i = 0; i < argv.length; i += 1) {
57
+ if (argv[i] === "--no-main") {
58
+ options.noMain = true;
59
+ }
60
+ }
61
+
62
+ return options;
63
+ }
64
+
65
+ function classify(branch) {
66
+ for (const pattern of BRANCH_PATTERNS) {
67
+ if (pattern.regex.test(branch)) {
68
+ return pattern.type;
69
+ }
70
+ }
71
+ return "unknown";
72
+ }
73
+
74
+ function main() {
75
+ const options = parseArgs(process.argv.slice(2));
76
+ const branch = detectBranch();
77
+
78
+ if (!branch) {
79
+ console.error("Branch strategy FAIL: cannot detect current git branch.");
80
+ process.exit(1);
81
+ }
82
+
83
+ const type = classify(branch);
84
+ if (type === "unknown") {
85
+ console.error(`Branch strategy FAIL: branch '${branch}' is not allowed by Git Flow Lite.`);
86
+ console.error("Allowed: main, develop, feature/*, release/*, hotfix/*");
87
+ process.exit(1);
88
+ }
89
+
90
+ if (options.noMain && type === "main") {
91
+ console.error("Branch strategy FAIL: direct operation on main is blocked by --no-main.");
92
+ process.exit(1);
93
+ }
94
+
95
+ console.log(`Branch strategy PASS: branch='${branch}', type='${type}'.`);
96
+ }
97
+
98
+ try {
99
+ main();
100
+ } catch (error) {
101
+ console.error(`Branch strategy FAIL: ${error.message}`);
102
+ process.exit(1);
103
+ }
@@ -1,4 +1,5 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ import { resolveTrackerForCommand } from "./tracker/provider-lock.mjs";
2
3
 
3
4
  function readLastCommitSubject(ref = "HEAD") {
4
5
  const output = execFileSync("git", ["log", "-1", "--pretty=%s", ref], { encoding: "utf8" });
@@ -12,7 +13,7 @@ function validateMessage(message) {
12
13
  }
13
14
 
14
15
  function parseArgs(argv) {
15
- const args = { message: "", ref: "HEAD" };
16
+ const args = { message: "", ref: "HEAD", tracker: "" };
16
17
  for (let i = 0; i < argv.length; i += 1) {
17
18
  if (argv[i] === "--message") {
18
19
  args.message = argv[i + 1] || "";
@@ -24,27 +25,40 @@ function parseArgs(argv) {
24
25
  i += 1;
25
26
  continue;
26
27
  }
28
+ if (argv[i] === "--tracker") {
29
+ args.tracker = argv[i + 1] || "";
30
+ i += 1;
31
+ continue;
32
+ }
27
33
  }
28
34
  return args;
29
35
  }
30
36
 
31
37
  function main() {
32
- const options = parseArgs(process.argv.slice(2));
33
- const message = options.message || readLastCommitSubject(options.ref);
38
+ try {
39
+ const options = parseArgs(process.argv.slice(2));
40
+ const trackerProvider = resolveTrackerForCommand({
41
+ requestedTracker: options.tracker,
42
+ cwd: process.cwd(),
43
+ });
44
+ const message = options.message || readLastCommitSubject(options.ref);
34
45
 
35
- if (!message) {
36
- console.error("Commit link FAIL: empty commit message.");
37
- process.exit(1);
38
- }
46
+ if (!message) {
47
+ console.error("Commit link FAIL: empty commit message.");
48
+ process.exit(1);
49
+ }
50
+
51
+ if (!validateMessage(message)) {
52
+ console.error("Commit link FAIL: message must contain TASK-ID or #issue.");
53
+ console.error(`Message: ${message}`);
54
+ process.exit(1);
55
+ }
39
56
 
40
- if (!validateMessage(message)) {
41
- console.error("Commit link FAIL: message must contain TASK-ID or #issue.");
42
- console.error(`Message: ${message}`);
57
+ console.log(`Commit link PASS [tracker=${trackerProvider}]: ${message}`);
58
+ } catch (error) {
59
+ console.error(`Commit link FAIL: ${error.message}`);
43
60
  process.exit(1);
44
61
  }
45
-
46
- console.log(`Commit link PASS: ${message}`);
47
62
  }
48
63
 
49
64
  main();
50
-
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
+ import { resolveLockedTrackerProvider } from "./tracker/provider-lock.mjs";
4
5
 
5
6
  const TASKS_DIR = path.resolve(process.cwd(), "tasks");
6
7
  const EXCLUDED_FILES = new Set(["backlog.md", "sprint-1.md", "task-status-log.md"]);
@@ -15,10 +16,14 @@ const REQUIRED_PATTERNS = [
15
16
  ];
16
17
 
17
18
  async function listTaskFiles() {
18
- const entries = await fs.readdir(TASKS_DIR, { withFileTypes: true });
19
- return entries
20
- .filter((entry) => entry.isFile() && entry.name.endsWith(".md") && !EXCLUDED_FILES.has(entry.name))
21
- .map((entry) => path.join(TASKS_DIR, entry.name));
19
+ try {
20
+ const entries = await fs.readdir(TASKS_DIR, { withFileTypes: true });
21
+ return entries
22
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".md") && !EXCLUDED_FILES.has(entry.name))
23
+ .map((entry) => path.join(TASKS_DIR, entry.name));
24
+ } catch {
25
+ return null;
26
+ }
22
27
  }
23
28
 
24
29
  async function validateTask(filePath) {
@@ -35,19 +40,38 @@ async function validateTask(filePath) {
35
40
  }
36
41
 
37
42
  async function main() {
38
- let taskFiles;
43
+ const args = process.argv.slice(2);
44
+ const validateLocalCache = args.includes("--validate-local-cache");
45
+
46
+ let trackerProvider = "";
39
47
  try {
40
- taskFiles = await listTaskFiles();
41
- } catch {
42
- console.error("Governance FAIL: tasks directory not found.");
48
+ trackerProvider = resolveLockedTrackerProvider(process.cwd());
49
+ } catch (error) {
50
+ console.error(`Governance FAIL: ${error.message}`);
43
51
  process.exit(1);
44
52
  }
45
53
 
46
- if (taskFiles.length === 0) {
47
- console.error("Governance FAIL: no task markdown files found in tasks/.");
54
+ const taskFiles = await listTaskFiles();
55
+ const localTasksRequired = trackerProvider === "local" || validateLocalCache;
56
+
57
+ if (!localTasksRequired) {
58
+ const localCount = taskFiles ? taskFiles.length : 0;
59
+ console.log(
60
+ `Governance PASS: tracker provider '${trackerProvider}' is external; local task validation skipped (${localCount} file(s) in tasks/).`,
61
+ );
62
+ return;
63
+ }
64
+
65
+ if (taskFiles === null) {
66
+ console.error("Governance FAIL: tasks directory not found for local-task validation.");
48
67
  process.exit(1);
49
68
  }
50
69
 
70
+ if (taskFiles.length === 0) {
71
+ console.log(`Governance PASS: no local task files found, tracker provider '${trackerProvider}'.`);
72
+ return;
73
+ }
74
+
51
75
  let hasErrors = false;
52
76
  for (const filePath of taskFiles) {
53
77
  const failed = await validateTask(filePath);
@@ -64,11 +88,12 @@ async function main() {
64
88
  process.exit(1);
65
89
  }
66
90
 
67
- console.log(`Governance PASS: checked ${taskFiles.length} task file(s).`);
91
+ console.log(
92
+ `Governance PASS: checked ${taskFiles.length} task file(s), tracker provider '${trackerProvider}'.`,
93
+ );
68
94
  }
69
95
 
70
96
  main().catch((error) => {
71
97
  console.error(`Governance FAIL: ${error.message}`);
72
98
  process.exit(1);
73
99
  });
74
-
@@ -10,5 +10,12 @@
10
10
  - Acceptance Criteria:
11
11
  - Failure/Escalation Rules:
12
12
  - Tracker Binding (task/status/sprint):
13
- - Git Strategy (main or feature branch + MR/PR):
13
+ - Git Strategy (Git Flow Lite: feature/* -> develop, release/* -> main + back-merge, hotfix/* -> main + back-merge):
14
+ - Branch Routing Preflight:
15
+ - Current Branch:
16
+ - Requested Task Type (atomic|feature|release|hotfix):
17
+ - Routing Decision (stay_on_current_branch|create_new_branch):
18
+ - Base Branch:
19
+ - Working Branch:
20
+ - User Dialogue Confirmed (yes/no):
14
21
  - Epic/Feature Linkage (for large features in GitHub tracker):
@@ -3,10 +3,16 @@
3
3
  - Task ID:
4
4
  - Short Name:
5
5
  - Owner Role:
6
+ - Task Type: atomic (atomic|feature|release|hotfix)
6
7
  - Status: TODO (TODO -> IN_PROGRESS -> REVIEW -> READY -> DONE -> PUBLISH for release tasks)
7
8
  - Sprint:
8
9
  - Parent Epic (optional):
9
- - Git Strategy (main or feature branch + MR/PR):
10
+ - Git Strategy (Git Flow Lite: feature/* -> develop, release/* -> main + back-merge, hotfix/* -> main + back-merge):
11
+ - Branch Context:
12
+ - Current Branch:
13
+ - Branch Task Ref:
14
+ - Routing Decision (stay_on_current_branch|create_new_branch):
15
+ - Working Branch (target):
10
16
 
11
17
  ## PRD Step
12
18
  ### Описание
@@ -6,14 +6,23 @@
6
6
  - `agents.md`: роли, правила и жёсткие критерии приёмки;
7
7
  - `tracker-command-template.md`: команды управления задачами и спринтами;
8
8
  - `.meta-agents/`: конфигурация системы и шаблоны PRD/verification;
9
+ - `.github/workflows/gitflow-lite-verify.yml`: branch-aware CI-проверки для Git Flow Lite;
9
10
  - `tasks/`: backlog/sprint/status-log.
10
11
 
12
+ ## Source of truth по tracker provider
13
+ - Выбранный при `init` provider фиксируется в `.meta-agents/config/project-context.yaml`.
14
+ - Он должен совпадать с `.meta-agents/config/trackers.yaml -> tracker_gateway.default`.
15
+ - По умолчанию все операционные команды выполняются только через этот provider, пока пользователь явно не изменит конфиг.
16
+
11
17
  Отдельно включён `Governance Watchdog Agent`:
12
18
  - обязательный gate перед каждой командой;
13
19
  - техническая проверка PRD-структуры через `npm run meta:verify`.
14
20
  - обязательный review gate перед коммитом:
15
21
  - `npm run meta:review` (отчёт + рекомендация);
16
22
  - `npm run meta:review-approve` (финальный PASS только после подтверждения пользователя).
23
+ - обязательный MR review gate перед merge:
24
+ - `npm run meta:mr-review` (сводный MR/PR анализ);
25
+ - `npm run meta:mr-review-approve` (финальный PASS только после подтверждения пользователя).
17
26
 
18
27
  ## Что спросит init
19
28
  1. Git режим: `github`, `local` или `skip`.
@@ -24,13 +33,15 @@
24
33
  1. Каждый шаг содержит: `Описание -> Проверяемость -> Что сделано`.
25
34
  2. Перед каждой задачей обязателен вывод `[Agent Auto-Select]` и `[Task Agent]`.
26
35
  3. Коммиты и закрытие задачи в `DONE` только после явного подтверждения пользователя.
27
- 4. В single-branch режиме используется статусный цикл: `TODO -> IN_PROGRESS -> REVIEW -> READY -> DONE` (для релизных задач дополнительно `-> PUBLISH`).
36
+ 4. В режиме Git Flow Lite используется статусный цикл: `TODO -> IN_PROGRESS -> REVIEW -> READY -> DONE` (для релизных задач дополнительно `-> PUBLISH`).
28
37
  5. Без подтверждения пользователя максимум статуса: `REVIEW`; `READY` только после `RUN_REVIEW_GATE: PASS_CONFIRMED`.
29
- 6. `READY` = коммит + push (в `main` или в ветку с открытым MR/PR); `DONE` = интеграция в `main`; `PUBLISH` = опубликовано в latest.
30
- 7. Для больших фич (GitHub tracker) агент обязан предложить `feature issue` + `epic issue` и отдельную ветку.
31
- 8. В GitHub epic оформляется как issue с label `type:epic`, а feature связывается с ним ссылкой (`type:feature`).
32
- 9. Для релиза используется `Publishing Agent`: он создаёт release note с вошедшими задачами и переводит эти задачи в `PUBLISH`.
33
- 10. Перед релизом `Publishing Agent` обязан обновить публичный `CHANGELOG.md`.
38
+ 6. Merge в целевую ветку разрешён только после `RUN_MR_REVIEW_GATE: PASS_CONFIRMED` и `MR Review Approved: yes`.
39
+ 7. Git Flow Lite ветки: `feature/* -> develop`, `release/* -> main` + back-merge в `develop`, `hotfix/* -> main` + back-merge в `develop`.
40
+ 8. `READY` = commit + push в `feature/*|release/*|hotfix/*` и открытый PR в целевую ветку; `DONE` = интеграция в `main` + back-merge (для release/hotfix); `PUBLISH` = опубликовано в latest.
41
+ 9. Для больших фич (GitHub tracker) агент обязан предложить `feature issue` + `epic issue` и отдельную ветку.
42
+ 10. В GitHub epic оформляется как issue с label `type:epic`, а feature связывается с ним ссылкой (`type:feature`).
43
+ 11. Для релиза используется `Publishing Agent`: он создаёт release note с вошедшими задачами и переводит эти задачи в `PUBLISH`.
44
+ 12. Перед релизом `Publishing Agent` обязан обновить публичный `CHANGELOG.md`.
34
45
 
35
46
  ## Команды
36
47
  Смотри `tracker-command-template.md`.
@@ -38,7 +49,33 @@
38
49
  Дополнительно:
39
50
  ```bash
40
51
  npm run meta:verify
52
+ npm run meta:branch
53
+ npm run meta:task-start -- --task 12 --slug api-redirect
41
54
  npm run meta:review
42
55
  npm run meta:review-approve
56
+ npm run meta:mr-review
57
+ npm run meta:mr-review-approve
43
58
  npm run meta:verify-link
59
+ npm run meta:ops -- --command SET_STATUS --payload '{"task":"DEV-12","status":"IN_PROGRESS"}'
60
+ npm run meta:ops -- --command PREPARE_TASK_BRANCH --payload '{"task":"12","slug":"api-redirect"}'
61
+ npm run meta:status
44
62
  ```
63
+
64
+ `meta:ops` принудительно валидирует tracker provider lock.
65
+ Если передать `--tracker`, он обязан совпадать с зафиксированным provider проекта, иначе команда блокируется.
66
+ `meta:branch` валидирует ветку по Git Flow Lite (`main`, `develop`, `feature/*`, `release/*`, `hotfix/*`).
67
+ `meta:task-start` делает branch-routing preflight: сравнивает задачу с текущей веткой, показывает dirty/ahead блокеры и готовит маршрут (`stay_on_current_branch` или `create_new_branch`).
68
+ `meta:task-start` также делает context-protection check для `agents.md` между текущей и базовой веткой и требует явного подтверждения при diff.
69
+ `meta:mr-review` формирует сводный pre-merge отчёт по MR/PR, `meta:mr-review-approve` фиксирует финальный PASS после подтверждения пользователя.
70
+
71
+ `meta:status` выдаёт сводный статус:
72
+ - trackers used;
73
+ - current sprint;
74
+ - current task;
75
+ - current branch context (type/task-ref/alignment);
76
+ - git uncommitted summary;
77
+ - короткая сводка.
78
+
79
+ Важно:
80
+ - в `github` режиме локальные `tasks/*` не считаются источником истины по умолчанию (только cache/legacy);
81
+ - в `local` режиме `tasks/*` считаются источником истины.