rodo-spec 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -170,6 +170,7 @@ The CLI executable is `rodo`. Global flags `--project <slug>`, `--lang <fa|en>`,
170
170
  | :--- | :--- | :--- |
171
171
  | `rodo init` | `[--method-only] [--project <slug>] [--name <name>] [--lang <fa\|en>] [--connect <code>] [--board <id>] [--create-board] [--server <url>] [--no-kit-update] [--dry-run] [--yes-files]` | Installs or updates the RODO kit, scaffolds `rodo/<project>/`, and optionally binds & syncs with a board. |
172
172
  | `rodo new` | `"<title>" [--slug <kebab-slug>] [--major] [--dry-run] [--project <slug>]` | Creates the next `rodo/<project>/cycles/NNN-slug/` folder with `card.rodo.md`, `simple.md`, and `spec.md`. |
173
+ | `rodo explain` | `[card] [--project <slug>]` | Describes a card in plain, non-technical words (what you get, state, steps left). |
173
174
  | `rodo resume` | `[card] [--full] [--project <slug>]` | Prints compact context of the active or specified card for an AI session (`--full` includes architecture & `simple.md`). |
174
175
  | `rodo validate` | `[paths...] [--strict] [--project <slug>]` | Validates card files and cycle references (`--strict` exits with code `1` on errors for CI). |
175
176
  | `rodo test` | `[card] [--adapter <web\|api\|cli\|mobile\|iot\|other>] [--project <slug>]` | Runs the card's test command and tracks repeated failure signatures (`RODO_STUCK`). |
package/dist/index.js CHANGED
@@ -11057,7 +11057,7 @@ function ensureBlock(path, block, kit20Hashes, importsAgents, dryRun = false) {
11057
11057
  // package.json
11058
11058
  var package_default = {
11059
11059
  name: "rodo-spec",
11060
- version: "2.3.0",
11060
+ version: "2.4.0",
11061
11061
  description: "RODO method CLI \u2014 install the kit, manage cards, sync with the board",
11062
11062
  license: "MIT",
11063
11063
  type: "module",
@@ -11470,8 +11470,8 @@ function parseHours(v) {
11470
11470
  function readFields(data, keyLine, order) {
11471
11471
  const m = emptyModel();
11472
11472
  const out = [];
11473
- const L = (k) => keyLine[k] ?? null;
11474
- const add = (code, k, args = {}) => out.push(finding(code, L(k), args));
11473
+ const L2 = (k) => keyLine[k] ?? null;
11474
+ const add = (code, k, args = {}) => out.push(finding(code, L2(k), args));
11475
11475
  for (const k of order) {
11476
11476
  if (KNOWN.has(k) || k.startsWith("x-"))
11477
11477
  continue;
@@ -13088,6 +13088,129 @@ async function runDoctor(ctx) {
13088
13088
  return 0;
13089
13089
  }
13090
13090
 
13091
+ // src/core/i18n.ts
13092
+ var T = {
13093
+ "resume.none": { fa: "\u06A9\u0627\u0631\u062A \u0641\u0639\u0627\u0644\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F", en: "no active card found" },
13094
+ "resume.several": { fa: "\u0686\u0646\u062F \u06A9\u0627\u0631\u062A \u0641\u0639\u0627\u0644 \u0647\u0633\u062A\u061B \u06CC\u06A9\u06CC \u0631\u0627 \u0645\u0634\u062E\u0635 \u06A9\u0646\u06CC\u062F", en: "several active cards; name one" },
13095
+ "resume.never": { fa: "\u0627\u06CC\u0646 \u067E\u0631\u0648\u0698\u0647 \u0647\u0646\u0648\u0632 \u0647\u06CC\u0686\u200C\u0648\u0642\u062A sync \u0646\u0634\u062F\u0647\u061B rodo sync \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F", en: "this project has never been synced; run rodo sync" },
13096
+ "resume.stale": { fa: "\u0622\u062E\u0631\u06CC\u0646 sync \u0628\u06CC\u0634 \u0627\u0632 \u06CC\u06A9 \u0633\u0627\u0639\u062A \u067E\u06CC\u0634 \u0628\u0648\u062F\u0647\u061B rodo sync \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F", en: "last sync is older than 1 hour; run rodo sync" },
13097
+ "status.waitingDecision": { fa: "\u0645\u0646\u062A\u0638\u0631 \u062A\u0635\u0645\u06CC\u0645 \u0634\u0645\u0627", en: "waiting for your decision" },
13098
+ "status.waitingApproval": { fa: "\u0645\u0646\u062A\u0638\u0631 \u062A\u0623\u06CC\u06CC\u062F \u0634\u0645\u0627", en: "waiting for approval" },
13099
+ "test.none": { fa: "\u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0631\u062A \u062F\u0633\u062A\u0648\u0631 \u062A\u0633\u062A \u062A\u0639\u0631\u06CC\u0641 \u0646\u0634\u062F\u0647 (\u062A\u0633\u062A \u0627\u062E\u062A\u06CC\u0627\u0631\u06CC \u0627\u0633\u062A)", en: "no test command for this card (tests are optional)" },
13100
+ "offline.note": { fa: "\u062D\u0627\u0644\u062A \u0622\u0641\u0644\u0627\u06CC\u0646: \u0641\u0642\u0637 \u0628\u0631\u0631\u0633\u06CC \u06A9\u0627\u0631\u062A\u200C\u0647\u0627 \u0627\u0646\u062C\u0627\u0645 \u0634\u062F", en: "offline: cards were validated only" }
13101
+ };
13102
+ function pickLang(flag, projectLang) {
13103
+ return flag === "fa" || flag === "en" ? flag : projectLang === "en" ? "en" : "fa";
13104
+ }
13105
+ var t = (lang, key2) => T[key2]?.[lang] ?? key2;
13106
+
13107
+ // src/commands/shared.ts
13108
+ var LEGACY_MSG = "LEGACY_LAYOUT: run rodo init to move rodo/ into rodo/<project>/";
13109
+ function load(ctx, defaultToCwd) {
13110
+ const d = discover(ctx.cwd);
13111
+ const refs = selectProjects(d, ctx.opts.project ?? [], defaultToCwd);
13112
+ const broken = refs.find((r) => r.problem);
13113
+ if (broken) throw new CliError("PROJECT_INVALID", `rodo/${broken.slug}: ${broken.problem}`, 3);
13114
+ if (!refs.length) throw notRepo("no RODO project found: run rodo init --method-only");
13115
+ return { d, refs };
13116
+ }
13117
+ var fmt = (c, f, icon) => ` ${icon} ${c.path}${f.line ? `:${f.line}` : ""} ${f.message}`;
13118
+ function printFindings(ctx, cards) {
13119
+ for (const c of cards) {
13120
+ for (const f of c.errors) ctx.out.line(fmt(c, f, mark.err));
13121
+ for (const f of c.warnings) ctx.out.line(fmt(c, f, mark.warn));
13122
+ }
13123
+ }
13124
+ var sum = (cards) => ({
13125
+ files: cards.length,
13126
+ errors: cards.reduce((n, c) => n + c.errors.length, 0),
13127
+ warnings: cards.reduce((n, c) => n + c.warnings.length, 0)
13128
+ });
13129
+ var slim = (f) => ({ code: f.code, line: f.line, message: f.message });
13130
+
13131
+ // src/commands/select.ts
13132
+ function oneProject(ctx) {
13133
+ const { d, refs } = load(ctx, true);
13134
+ if (refs.length !== 1) throw usage(`several projects (${refs.map((r) => r.slug).join(", ")}): use --project <slug>`);
13135
+ return { d, ref: refs[0] };
13136
+ }
13137
+ function pickCard(cards, query) {
13138
+ if (query) {
13139
+ const c = findCard(cards, query);
13140
+ if (!c) throw usage(`no card matches "${query}"`);
13141
+ return c;
13142
+ }
13143
+ for (const status of ["in_progress", "blocked"]) {
13144
+ const list = cards.filter((c) => c.model?.status === status);
13145
+ if (list.length === 1) return list[0];
13146
+ if (list.length > 1) throw usage(`several ${status} cards, name one:
13147
+ ${list.map((c) => ` ${c.number ?? c.key} ${c.model?.title}`).join("\n")}`);
13148
+ }
13149
+ throw new CliError("NO_ACTIVE_CARD", "no in_progress or blocked card; name one (rodo resume <card>)", 2);
13150
+ }
13151
+ function projectCards(ctx) {
13152
+ const { d, ref } = oneProject(ctx);
13153
+ return { d, ref, cards: scanCards(d.root, ref) };
13154
+ }
13155
+
13156
+ // src/commands/explain.ts
13157
+ var L = {
13158
+ status_todo: { fa: "\u0628\u0631\u0646\u0627\u0645\u0647\u200C\u0631\u06CC\u0632\u06CC \u0634\u062F\u0647\u060C \u0647\u0646\u0648\u0632 \u0634\u0631\u0648\u0639 \u0646\u0634\u062F\u0647", en: "planned, not started yet" },
13159
+ status_in_progress: { fa: "\u062F\u0631 \u062D\u0627\u0644 \u0627\u0646\u062C\u0627\u0645", en: "in progress" },
13160
+ status_blocked: { fa: "\u0645\u062A\u0648\u0642\u0641 \u0634\u062F\u0647", en: "stopped" },
13161
+ status_cancelled: { fa: "\u0644\u063A\u0648 \u0634\u062F\u0647", en: "cancelled" },
13162
+ status_done: { fa: "\u062A\u0645\u0627\u0645 \u0634\u062F\u0647", en: "finished" },
13163
+ what: { fa: "\u0686\u0647 \u0686\u06CC\u0632\u06CC \u0645\u06CC\u200C\u06AF\u06CC\u0631\u06CC\u062F", en: "What you get" },
13164
+ state: { fa: "\u0648\u0636\u0639\u06CC\u062A", en: "State" },
13165
+ steps: { fa: "\u0642\u062F\u0645\u200C\u0647\u0627", en: "Steps" },
13166
+ left: { fa: "\u0628\u0627\u0642\u06CC\u200C\u0645\u0627\u0646\u062F\u0647", en: "Still to do" },
13167
+ why: { fa: "\u062F\u0644\u06CC\u0644 \u062A\u0648\u0642\u0641", en: "Why it stopped" },
13168
+ noText: { fa: "\u0647\u0646\u0648\u0632 \u062A\u0648\u0636\u06CC\u062D \u0633\u0627\u062F\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0631\u062A \u0646\u0648\u0634\u062A\u0647 \u0646\u0634\u062F\u0647", en: "no plain description has been written for this card yet" }
13169
+ };
13170
+ function intro(body) {
13171
+ const out = [];
13172
+ for (const raw of body.split(/\r?\n/)) {
13173
+ if (raw.startsWith("## ")) break;
13174
+ const l = raw.trim();
13175
+ if (l && !/\{\{.*\}\}/.test(l)) out.push(l);
13176
+ }
13177
+ return out;
13178
+ }
13179
+ async function runExplain(ctx, query) {
13180
+ const { ref, cards } = projectCards(ctx);
13181
+ const card = pickCard(cards, query);
13182
+ const m = card.model;
13183
+ if (!m) {
13184
+ ctx.out.line(`${card.path} is not a readable card:`);
13185
+ for (const e2 of card.errors) ctx.out.line(` ${e2.message}`);
13186
+ ctx.out.flush({ version: 1, card: card.key, errors: card.errors });
13187
+ return 2;
13188
+ }
13189
+ const lang = pickLang(ctx.opts.lang, ref.project?.language.simple);
13190
+ const tr = (k) => L[k][lang];
13191
+ const items = m.checklists.flatMap((c) => c.items);
13192
+ const done = items.filter((i) => i.isDone).length;
13193
+ const left = items.filter((i) => !i.isDone).map((i) => i.title).slice(0, 5);
13194
+ const text = intro(m.description);
13195
+ const title = card.number ? cardLabel(card.number, m.title, " \u2014 ") : m.title;
13196
+ ctx.out.line(title);
13197
+ ctx.out.line(`${tr("state")}: ${tr(`status_${m.status}`)}`);
13198
+ if (m.statusReason) ctx.out.line(`${tr("why")}: ${m.statusReason}`);
13199
+ ctx.out.line("");
13200
+ ctx.out.line(`${tr("what")}:`);
13201
+ (text.length ? text : [tr("noText")]).forEach((l) => ctx.out.line(` ${l}`));
13202
+ if (items.length) {
13203
+ ctx.out.line("");
13204
+ ctx.out.line(`${tr("steps")}: ${done}/${items.length}`);
13205
+ if (left.length) {
13206
+ ctx.out.line(`${tr("left")}:`);
13207
+ left.forEach((l) => ctx.out.line(` - ${l}`));
13208
+ }
13209
+ }
13210
+ ctx.out.flush({ version: 1, card: card.key, title: m.title, status: m.status, statusReason: m.statusReason ?? null, plain: text, steps: { done, total: items.length }, remaining: left });
13211
+ return 0;
13212
+ }
13213
+
13091
13214
  // src/commands/init.ts
13092
13215
  import { existsSync as existsSync14 } from "fs";
13093
13216
  import { basename as basename2, join as join14 } from "path";
@@ -13259,13 +13382,13 @@ function withMarker(raw, marker) {
13259
13382
  const next = bom + (marker ? marker + eol : "") + rest;
13260
13383
  return next === raw ? null : next;
13261
13384
  }
13262
- var slim = (f) => ({ code: f.code, line: f.line, message: f.message });
13385
+ var slim2 = (f) => ({ code: f.code, line: f.line, message: f.message });
13263
13386
  function buildReport(slug2, cards, startedAt, over = {}, extra = /* @__PURE__ */ new Map()) {
13264
13387
  const merged = cards.map((c) => {
13265
13388
  const x = extra.get(c.path);
13266
13389
  return { c, errors: [...c.errors, ...x?.errors ?? []], warnings: [...c.warnings, ...x?.warnings ?? []] };
13267
13390
  });
13268
- const files = merged.filter((m) => m.errors.length || m.warnings.length).map((m) => ({ path: m.c.path, cardId: m.c.model?.id ?? null, errors: m.errors.map(slim), warnings: m.warnings.map(slim) }));
13391
+ const files = merged.filter((m) => m.errors.length || m.warnings.length).map((m) => ({ path: m.c.path, cardId: m.c.model?.id ?? null, errors: m.errors.map(slim2), warnings: m.warnings.map(slim2) }));
13269
13392
  return {
13270
13393
  version: 1,
13271
13394
  project: slug2,
@@ -14022,7 +14145,7 @@ async function runInit(ctx, a) {
14022
14145
  out.line(`project rodo/${slug2}/: ${existsSync14(join14(projectDir, "project.json")) ? "exists (missing files would be added)" : "would be created"}`);
14023
14146
  } else {
14024
14147
  const isNew = !existsSync14(join14(projectDir, "project.json"));
14025
- const lang = isNew ? await chooseLanguage(ctx) : readProject(projectDir, slug2).project?.language.simple ?? "fa";
14148
+ const lang = isNew ? await chooseLanguage(ctx) : "fa";
14026
14149
  const created = scaffoldProject(root, slug2, a.name ?? slug2, lang);
14027
14150
  if (isNew) data.language = lang;
14028
14151
  out.line(created.length ? `rodo/${slug2}/: created ${created.length} file(s)` : `rodo/${slug2}/: nothing to add`);
@@ -14069,32 +14192,6 @@ async function chooseLanguage(ctx, env = process.env) {
14069
14192
  // src/commands/new.ts
14070
14193
  import { existsSync as existsSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
14071
14194
  import { join as join15, relative as relative5, sep as sep2 } from "path";
14072
-
14073
- // src/commands/shared.ts
14074
- var LEGACY_MSG = "LEGACY_LAYOUT: run rodo init to move rodo/ into rodo/<project>/";
14075
- function load(ctx, defaultToCwd) {
14076
- const d = discover(ctx.cwd);
14077
- const refs = selectProjects(d, ctx.opts.project ?? [], defaultToCwd);
14078
- const broken = refs.find((r) => r.problem);
14079
- if (broken) throw new CliError("PROJECT_INVALID", `rodo/${broken.slug}: ${broken.problem}`, 3);
14080
- if (!refs.length) throw notRepo("no RODO project found: run rodo init --method-only");
14081
- return { d, refs };
14082
- }
14083
- var fmt = (c, f, icon) => ` ${icon} ${c.path}${f.line ? `:${f.line}` : ""} ${f.message}`;
14084
- function printFindings(ctx, cards) {
14085
- for (const c of cards) {
14086
- for (const f of c.errors) ctx.out.line(fmt(c, f, mark.err));
14087
- for (const f of c.warnings) ctx.out.line(fmt(c, f, mark.warn));
14088
- }
14089
- }
14090
- var sum = (cards) => ({
14091
- files: cards.length,
14092
- errors: cards.reduce((n, c) => n + c.errors.length, 0),
14093
- warnings: cards.reduce((n, c) => n + c.warnings.length, 0)
14094
- });
14095
- var slim2 = (f) => ({ code: f.code, line: f.line, message: f.message });
14096
-
14097
- // src/commands/new.ts
14098
14195
  function slugify2(title) {
14099
14196
  return title.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").split("-").slice(0, 6).join("-");
14100
14197
  }
@@ -14159,22 +14256,6 @@ async function runNew(ctx, title, o) {
14159
14256
  import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
14160
14257
  import { dirname as dirname8, join as join17 } from "path";
14161
14258
 
14162
- // src/core/i18n.ts
14163
- var T = {
14164
- "resume.none": { fa: "\u06A9\u0627\u0631\u062A \u0641\u0639\u0627\u0644\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F", en: "no active card found" },
14165
- "resume.several": { fa: "\u0686\u0646\u062F \u06A9\u0627\u0631\u062A \u0641\u0639\u0627\u0644 \u0647\u0633\u062A\u061B \u06CC\u06A9\u06CC \u0631\u0627 \u0645\u0634\u062E\u0635 \u06A9\u0646\u06CC\u062F", en: "several active cards; name one" },
14166
- "resume.never": { fa: "\u0627\u06CC\u0646 \u067E\u0631\u0648\u0698\u0647 \u0647\u0646\u0648\u0632 \u0647\u06CC\u0686\u200C\u0648\u0642\u062A sync \u0646\u0634\u062F\u0647\u061B rodo sync \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F", en: "this project has never been synced; run rodo sync" },
14167
- "resume.stale": { fa: "\u0622\u062E\u0631\u06CC\u0646 sync \u0628\u06CC\u0634 \u0627\u0632 \u06CC\u06A9 \u0633\u0627\u0639\u062A \u067E\u06CC\u0634 \u0628\u0648\u062F\u0647\u061B rodo sync \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F", en: "last sync is older than 1 hour; run rodo sync" },
14168
- "status.waitingDecision": { fa: "\u0645\u0646\u062A\u0638\u0631 \u062A\u0635\u0645\u06CC\u0645 \u0634\u0645\u0627", en: "waiting for your decision" },
14169
- "status.waitingApproval": { fa: "\u0645\u0646\u062A\u0638\u0631 \u062A\u0623\u06CC\u06CC\u062F \u0634\u0645\u0627", en: "waiting for approval" },
14170
- "test.none": { fa: "\u0628\u0631\u0627\u06CC \u0627\u06CC\u0646 \u06A9\u0627\u0631\u062A \u062F\u0633\u062A\u0648\u0631 \u062A\u0633\u062A \u062A\u0639\u0631\u06CC\u0641 \u0646\u0634\u062F\u0647 (\u062A\u0633\u062A \u0627\u062E\u062A\u06CC\u0627\u0631\u06CC \u0627\u0633\u062A)", en: "no test command for this card (tests are optional)" },
14171
- "offline.note": { fa: "\u062D\u0627\u0644\u062A \u0622\u0641\u0644\u0627\u06CC\u0646: \u0641\u0642\u0637 \u0628\u0631\u0631\u0633\u06CC \u06A9\u0627\u0631\u062A\u200C\u0647\u0627 \u0627\u0646\u062C\u0627\u0645 \u0634\u062F", en: "offline: cards were validated only" }
14172
- };
14173
- function pickLang(flag, projectLang) {
14174
- return flag === "fa" || flag === "en" ? flag : projectLang === "en" ? "en" : "fa";
14175
- }
14176
- var t = (lang, key2) => T[key2]?.[lang] ?? key2;
14177
-
14178
14259
  // src/core/time.ts
14179
14260
  var fmtFor = (timeZone) => new Intl.DateTimeFormat("sv-SE", {
14180
14261
  timeZone,
@@ -14431,31 +14512,6 @@ function streak(previous, sig) {
14431
14512
  }
14432
14513
  var STUCK_MESSAGE = (sig) => `RODO_STUCK: the same failure happened twice (${sig}). Per the RODO method: set status: blocked, blockerType: test or technical, write the reason in statusReason, and ask the user.`;
14433
14514
 
14434
- // src/commands/select.ts
14435
- function oneProject(ctx) {
14436
- const { d, refs } = load(ctx, true);
14437
- if (refs.length !== 1) throw usage(`several projects (${refs.map((r) => r.slug).join(", ")}): use --project <slug>`);
14438
- return { d, ref: refs[0] };
14439
- }
14440
- function pickCard(cards, query) {
14441
- if (query) {
14442
- const c = findCard(cards, query);
14443
- if (!c) throw usage(`no card matches "${query}"`);
14444
- return c;
14445
- }
14446
- for (const status of ["in_progress", "blocked"]) {
14447
- const list = cards.filter((c) => c.model?.status === status);
14448
- if (list.length === 1) return list[0];
14449
- if (list.length > 1) throw usage(`several ${status} cards, name one:
14450
- ${list.map((c) => ` ${c.number ?? c.key} ${c.model?.title}`).join("\n")}`);
14451
- }
14452
- throw new CliError("NO_ACTIVE_CARD", "no in_progress or blocked card; name one (rodo resume <card>)", 2);
14453
- }
14454
- function projectCards(ctx) {
14455
- const { d, ref } = oneProject(ctx);
14456
- return { d, ref, cards: scanCards(d.root, ref) };
14457
- }
14458
-
14459
14515
  // src/commands/resume.ts
14460
14516
  var MAP_WHAT = /^##\s*(این پروژه چیست|What this project is)/i;
14461
14517
  var MAP_WHERE = /^##\s*(الان کجاییم|Where we are)/i;
@@ -14769,18 +14825,7 @@ async function runTest(ctx, query, a) {
14769
14825
  }
14770
14826
 
14771
14827
  // src/commands/validate.ts
14772
- import { existsSync as existsSync18, readFileSync as readFileSync17 } from "fs";
14773
- import { join as join19, resolve as resolve3 } from "path";
14774
- var PROJECT_DOC_LIMITS = { "project/project-overview.md": 500, "project/how-it-works.md": 900 };
14775
- var wordCount = (text) => text.replace(/<!--[\s\S]*?-->/g, " ").split(/\s+/).filter(Boolean).length;
14776
- function longProjectDocs(dir) {
14777
- return Object.entries(PROJECT_DOC_LIMITS).flatMap(([path, limit]) => {
14778
- const abs = join19(dir, path);
14779
- if (!existsSync18(abs)) return [];
14780
- const words = wordCount(readFileSync17(abs, "utf8"));
14781
- return words > limit ? [{ path, code: "PROJECT_DOC_TOO_LONG", line: null, message: `PROJECT_DOC_TOO_LONG: ${words} words, the limit is ${limit}; shorten it` }] : [];
14782
- });
14783
- }
14828
+ import { resolve as resolve3 } from "path";
14784
14829
  async function runValidate(ctx, paths, o = {}) {
14785
14830
  const { d, refs } = load(ctx, false);
14786
14831
  const only = paths.map((p) => resolve3(ctx.cwd, p));
@@ -14789,19 +14834,13 @@ async function runValidate(ctx, paths, o = {}) {
14789
14834
  for (const ref of refs) {
14790
14835
  const cards = scanCards(d.root, ref, only);
14791
14836
  const s = sum(cards);
14792
- const docs = only.length || ref.legacy ? [] : longProjectDocs(ref.dir);
14793
- s.warnings += docs.length;
14794
14837
  total.files += s.files;
14795
14838
  total.errors += s.errors;
14796
14839
  total.warnings += s.warnings;
14797
14840
  if (ref.legacy) ctx.out.line(LEGACY_MSG);
14798
14841
  ctx.out.line(`rodo/${ref.legacy ? "" : ref.slug} \u25B8 ${s.files} files \xB7 errors ${s.errors} \xB7 warnings ${s.warnings}`);
14799
14842
  printFindings(ctx, cards);
14800
- for (const c of cards) files.push({ project: ref.slug, path: c.path, errors: c.errors.map(slim2), warnings: c.warnings.map(slim2) });
14801
- for (const f of docs) {
14802
- ctx.out.line(` ${mark.warn} ${f.path} ${f.message}`);
14803
- files.push({ project: ref.slug, path: f.path, errors: [], warnings: [{ code: f.code, line: f.line, message: f.message }] });
14804
- }
14843
+ for (const c of cards) files.push({ project: ref.slug, path: c.path, errors: c.errors.map(slim), warnings: c.warnings.map(slim) });
14805
14844
  }
14806
14845
  ctx.out.flush({ version: 2, files, summary: total });
14807
14846
  return o.strict && total.errors > 0 ? 1 : 0;
@@ -14816,6 +14855,7 @@ Getting started:
14816
14855
  rodo new "Delete a note" start the next cycle (card, simple.md, spec.md)
14817
14856
  rodo validate --strict check the cards (exit 1 on errors)
14818
14857
  rodo resume <card> context for an AI session
14858
+ rodo explain <card> the card in plain words, for you
14819
14859
  rodo test <card> run the card test and catch repeated failures
14820
14860
  rodo init --project <slug> --create-board later: connect a board, then rodo sync`);
14821
14861
  program2.command("init").description("install or update the method kit and create a project folder").option("--name <name>", "display name").option("--server <url>", "board server (default: RODO_SERVER, else the built-in server)").option("--connect <code>", "one-time connect code from the board website (skips the device code)").option("--board <id>", "bind to an existing board").option("--create-board", "create a new board for this project").option("--method-only", "no server: install the kit and create the project folder only").option("--no-kit-update", "skip the kit step").option("--dry-run", "print the plan, write nothing").option("--yes-files", "answer yes to the .gitignore question").addHelpText("after", `
@@ -14832,6 +14872,7 @@ Examples:
14832
14872
  program2.command("status").description("cards and their state").option("--local").action((o) => run((ctx) => runStatus(ctx, o)));
14833
14873
  program2.command("doctor").description("read-only health checks").action(() => run((ctx) => runDoctor(ctx)));
14834
14874
  program2.command("resume [card]").description("print the context of a card for an AI session").option("--full").action((card, o) => run((ctx) => runResume(ctx, card, o)));
14875
+ program2.command("explain [card]").description("describe a card in plain words for the user (read-only)").action((card) => run((ctx) => runExplain(ctx, card)));
14835
14876
  program2.command("test [card]").description("run the card test command and track failure signatures").option("--adapter <adapter>", "web|api|cli|mobile|iot|other").action((card, o) => run((ctx) => runTest(ctx, card, o)));
14836
14877
  program2.command("version").description("versions").action(() => run((ctx) => runVersion(ctx)));
14837
14878
  return program2;
@@ -5,7 +5,7 @@ description: RODO project method. Use whenever the repo has a rodo/ folder or *.
5
5
 
6
6
  > یادداشت فارسی: این مهارت برای AI است. نسخهٔ انسانی همین قواعد در `rodo/<project>/RULES.md` است. RODO فقط «روش کار» است: هیچ ابزاری جلوی commit، تست یا کار را نمی‌گیرد.
7
7
 
8
- # RODO method (kit 2.3.0)
8
+ # RODO method (kit 2.4.0)
9
9
 
10
10
  RODO is a way of working, not a gate. Nothing blocks a commit, a test or a status. You follow it because it keeps the project understandable for the user, for teammates and for the next AI session.
11
11
 
@@ -86,7 +86,8 @@ A card may have `scope: {allowed: [...], forbidden: [...]}`. Stay inside `allowe
86
86
  | The repo has code but no `rodo/<project>/` yet | `onboarding.md` |
87
87
  | Typo, style, refactor or a fix that does not change described behaviour | `small-change.md` |
88
88
  | You write or fix tests (optional, recommended) | `testing.md` |
89
- | You write or update `project/project-overview.md` or `project/how-it-works.md` | `project-docs.md` |
89
+ | Before closing any card: keep the code small and clean | `clean.md` |
90
+ | Review before closing a MAJOR card, or on request | `review.md` |
90
91
 
91
92
  ## Workflow levels
92
93
 
@@ -123,7 +124,7 @@ Do not turn ordinary clarification questions into `## Decision:` sections, and d
123
124
 
124
125
  | Kind | Files | Rule |
125
126
  |---|---|---|
126
- | **CURRENT** (canonical) | `rodo/<project>/project/*`, `MAP.md`, `MEMORY.md` | Must describe the code as it is now. Update them in the same work. `MEMORY.md` is append-only: add a new line that says which line it replaces; never rewrite old lines. One exception: a planned part in `project-overview.md` (unticked) or `how-it-works.md` (marked `(planned)`) may describe work not built yet (`project-docs.md`). |
127
+ | **CURRENT** (canonical) | `rodo/<project>/project/*`, `MAP.md`, `MEMORY.md` | Must describe the code as it is now. Update them in the same work. `MEMORY.md` is append-only: add a new line that says which line it replaces; never rewrite old lines. |
127
128
  | **ACTIVE CYCLE** | the docs of the card you are working on (`simple.md`, `spec.md`, `design.md`, `acceptance.feature`) | Must match the code when the card is done. |
128
129
  | **HISTORICAL** | docs of `done` and `cancelled` cycles | Never rewritten. A later card refers to them; a new decision goes into the new card and `MEMORY.md`. |
129
130
 
@@ -143,8 +144,6 @@ Run the checks that fit this change with the project's own tools; RODO runs noth
143
144
  project.json board binding and settings (no secrets)
144
145
  MAP.md MEMORY.md RULES.md CHANGELOG.md
145
146
  project/{brief,architecture,glossary}.md
146
- project/project-overview.md the project for a client, parts with ticks
147
- project/how-it-works.md how each part works, for a beginner
148
147
  cycles/NNN-slug/
149
148
  card.rodo.md the card (synced with the board, not in git)
150
149
  simple.md plain explanation (shown read-only on the web)
@@ -162,6 +161,7 @@ Card format details: the comments in `templates/card.rodo.md` and `rodo validate
162
161
  | `rodo sync` | start and end of every session |
163
162
  | `rodo resume [card] [--full]` | context for a new session |
164
163
  | `rodo status` | board vs local, side by side |
164
+ | `rodo explain [card]` | the card in plain words, for the user |
165
165
  | `rodo test [card]` | run the card's test command, record the failure signature |
166
166
  | `rodo new "<title>"` | create the next cycle folder with `card.rodo.md`, `simple.md`, `spec.md` (`--major` adds `design.md` and `acceptance.feature`) |
167
167
  | `rodo validate` | check card files (never blocks; `--strict` exits 1 on errors, for CI) |
@@ -0,0 +1,33 @@
1
+ # Clean code formulas
2
+
3
+ Short rules for code you write or touch in a card. They keep the project small and easy to extend. Apply them to the files of this card only; never start a repo-wide cleanup without a card of its own.
4
+
5
+ ## The formulas
6
+
7
+ | Rule | Limit |
8
+ |---|---|
9
+ | One function does one thing | about 30 lines |
10
+ | One file has one reason to change | about 300 lines; split by responsibility, not by size alone |
11
+ | Parameters | 3 or fewer; more → pass one object |
12
+ | Nesting | 3 levels or fewer; use early `return` |
13
+ | Repetition | the third copy becomes a shared function; two copies stay (do not guess early) |
14
+ | Names | say what, not how (`overdueInvoices`, not `list2`); no abbreviations the glossary does not know |
15
+ | Comments | only the *why*; delete comments that repeat the code |
16
+ | Dead code | delete it (git remembers); never comment code out |
17
+ | New dependency | only if it replaces more than ~50 lines you would write and keep; mention it in `spec.md` `## Decisions` |
18
+ | Extension | add a new file or a new entry in a list, not a new `if` inside a long function |
19
+
20
+ ## Order of work (every card)
21
+
22
+ 1. Make it work (R-rules pass).
23
+ 2. Read your own diff once. Apply the formulas above to what you changed.
24
+ 3. Remove what is now unused: imports, variables, files, docs that describe the removed behaviour.
25
+ 4. Match the surrounding style (naming, comment density, error handling). Do not reformat code you did not change.
26
+ 5. Re-run the checks. Cleaning must not change behaviour.
27
+ 6. Tick the card's `Cleanup done` step and write `- Cleaned: <what you simplified, or "nothing needed">` in Progress notes.
28
+
29
+ ## Boundaries
30
+
31
+ - A cleanup that touches files outside the card's `scope.allowed` is a new card (SKILL.md "Scope").
32
+ - If a formula forces a large rewrite, do not do it silently: write `- Out of scope: <refactor> → new card` and tell the user.
33
+ - Prefer deleting over adding. The best change is the one with fewer lines for the same behaviour.
@@ -1,83 +1,86 @@
1
- > یادداشت فارسی: دستورالعمل کدنویس. از روی کارت کار کن، فقط ارجاع‌ها را بخوان، قدم‌به‌قدم جلو برو، سندها را درست نگه دار، و آخر هر جلسه «Progress notes» را به‌روز کن. همهٔ مسیرها نسبت به `rodo/<project>/` هستند.
2
-
3
- # Coder playbook
4
-
5
- You implement one card. Any model may do this; the user chooses.
6
-
7
- ## Before you start (every session)
8
-
9
- Follow "Session start" in SKILL.md (git check without pulling → rodo sync → rodo resume → refs only → approval check → answered decisions). Then set `status: in_progress`.
10
-
11
- ## Doing the steps
12
-
13
- For each unticked step, in order:
14
- 1. Do only what the step says.
15
- 2. Check it (unit test, build, or the command the step names).
16
- 3. Tick it `- [x]`; add `[hours: H:MM]` only if you know the real time.
17
- 4. Commit with the card number in the message, e.g. `feat: discount code input [cycle 002]`, so people can find the work later.
18
-
19
- If your change makes a project doc wrong (`project/*`, the card's `spec.md`), fix the doc in the same commit.
20
-
21
- ## Tests (if the card has scenarios)
22
-
23
- - Step definitions go in the folder from `project.json` `stepsDirs`. They must really drive the app (browser, API or CLI) and really assert. See `testing.md`.
24
- - Run the card's tests with `rodo test` (it uses the card's `test.command`, or `testCommand` from `project.json`). Tick `تست پذیرش پاس شد` only after a real passing run.
25
- - **Never weaken a scenario or test to make code pass:** no deleting scenarios, loosening assertions, `@skip`/`.only`/`test.fixme`, or changing an expected value to match buggy output. If a scenario looks wrong, explain why to the user; change it only if they agree.
26
-
27
- ## Scope changes while coding
28
-
29
- - Out of the card's `scope` or a new requirement: do not do it silently. Write `- Out of scope: <item> → new card` in Progress notes and tell the user.
30
- - Small change in the same feature: update the scenario/spec/steps of this card, tell the user, continue (wait if there is an unticked approval item).
31
- - Big new feature: do not add it. Write it under `- Remaining:` as "needs a new card" and tell the user.
32
-
33
- ## Stuck
34
-
35
- Use the stuck rule in SKILL.md: same failure signature twice → `status: blocked`, `blockerType: test` or `technical`, a plain `statusReason:`, ask the user. Waiting for an answer, a key or another card → `status: blocked` with the matching `blockerType` (`decision`, `permission`, `external`). If only the user can choose, write a `## Decision:` section (SKILL.md). When solved: `status: in_progress`, remove `statusReason` and `blockerType`. Never set `cancelled` yourself; only with the user's OK.
36
-
37
- ## Verify and fix
38
-
39
- **VERIFY** — pick the checks that fit this change and run them with the project's own tools (RODO runs nothing itself):
40
- - build, typecheck, lint;
41
- - unit, integration, e2e;
42
- - architecture rules, security checks;
43
- - acceptance (the card's `test` command via `rodo test`, or the acceptance criteria checked one by one);
44
- - documentation consistency (below).
45
-
46
- Write the result in Progress notes as `- Verified: <check> ✓/✗, …`.
47
-
48
- **Documentation Consistency** (part of VERIFY):
49
- - Check that every file listed under Documentation Impact was updated.
50
- - Check that the CURRENT docs and the active cycle docs still describe the code.
51
- - If something does not match: FIX, then VERIFY again.
52
- - If a current doc contradicts a `MEMORY.md` decision, stop and ask the user (as small-change.md already says).
53
-
54
- **REVIEW (optional; recommended for MAJOR)** — an independent AI session or model reads the change and checks:
55
- - requirement coverage; acceptance criteria;
56
- - regressions; architecture fit;
57
- - missing tests; maintainability;
58
- - unintended changes.
59
-
60
- A review never replaces tests. Its findings go back to FIX.
61
-
62
- **FIX loop** — FIX → VERIFY until everything passes (→ DONE) or the stuck rule applies: the **same failure twice** (same failure signature, or the same error at the same place) → `status: blocked` with `blockerType: test` or `technical` (SKILL.md "Stuck rule"). Different failures do not count.
63
-
64
- ## End of every session
65
-
66
- Follow "Session close" in SKILL.md. Always finish with `rodo sync`.
67
-
68
- ## Close (all steps ticked)
69
-
70
- The folder does not move.
71
- 1. Card: `status: done`.
72
- 2. `MAP.md`: add the cycle under `## انجام‌شده` (one line, link to simple.md or the card folder) and update "الان کجاییم".
73
- 3. `MEMORY.md`: append each `## Decisions` line of spec.md as `- YYYY-MM-DD — <decision>. چون <reason>. (cycle NNN)`.
74
- 4. `project/project-overview.md` and `project/how-it-works.md`: make them match what was built and tick the part if all of it is finished (`project-docs.md`).
75
- 5. Commit the MAP, MEMORY and project docs change. Later changes to this behaviour need a new card; changes that do not touch behaviour go to `small-change.md`.
76
-
77
- ## Rules
78
-
79
- - Never tick an approval item or change its text. Only a person can approve, in the web app. Never answer a `## Decision:` yourself.
80
- - Never edit `id:` lines or `<!-- rodo:... -->` comments.
81
- - Do not change `members`, `startAt`, `dueAt`, `priority` unless the user asks: the team owns them.
82
- - Never delete files you did not create in this card without asking.
83
- - Keep changes inside the steps. Unrelated fixes go to `small-change.md` or a new card.
1
+ > یادداشت فارسی: دستورالعمل کدنویس. از روی کارت کار کن، فقط ارجاع‌ها را بخوان، قدم‌به‌قدم جلو برو، سندها را درست نگه دار، و آخر هر جلسه «Progress notes» را به‌روز کن. همهٔ مسیرها نسبت به `rodo/<project>/` هستند.
2
+
3
+ # Coder playbook
4
+
5
+ You implement one card. Any model may do this; the user chooses.
6
+
7
+ ## Before you start (every session)
8
+
9
+ Follow "Session start" in SKILL.md (git check without pulling → rodo sync → rodo resume → refs only → approval check → answered decisions). Then set `status: in_progress`.
10
+
11
+ ## Doing the steps
12
+
13
+ For each unticked step, in order:
14
+ 1. Do only what the step says.
15
+ 2. Check it (unit test, build, or the command the step names).
16
+ 3. Tick it `- [x]`; add `[hours: H:MM]` only if you know the real time.
17
+ 4. Commit with the card number in the message, e.g. `feat: discount code input [cycle 002]`, so people can find the work later.
18
+
19
+ If your change makes a project doc wrong (`project/*`, the card's `spec.md`), fix the doc in the same commit.
20
+
21
+ ## Tests (if the card has scenarios)
22
+
23
+ - Step definitions go in the folder from `project.json` `stepsDirs`. They must really drive the app (browser, API or CLI) and really assert. See `testing.md`.
24
+ - Run the card's tests with `rodo test` (it uses the card's `test.command`, or `testCommand` from `project.json`). Tick `تست پذیرش پاس شد` only after a real passing run.
25
+ - **Never weaken a scenario or test to make code pass:** no deleting scenarios, loosening assertions, `@skip`/`.only`/`test.fixme`, or changing an expected value to match buggy output. If a scenario looks wrong, explain why to the user; change it only if they agree.
26
+
27
+ ## Scope changes while coding
28
+
29
+ - Out of the card's `scope` or a new requirement: do not do it silently. Write `- Out of scope: <item> → new card` in Progress notes and tell the user.
30
+ - Small change in the same feature: update the scenario/spec/steps of this card, tell the user, continue (wait if there is an unticked approval item).
31
+ - Big new feature: do not add it. Write it under `- Remaining:` as "needs a new card" and tell the user.
32
+
33
+ ## Stuck
34
+
35
+ Use the stuck rule in SKILL.md: same failure signature twice → `status: blocked`, `blockerType: test` or `technical`, a plain `statusReason:`, ask the user. Waiting for an answer, a key or another card → `status: blocked` with the matching `blockerType` (`decision`, `permission`, `external`). If only the user can choose, write a `## Decision:` section (SKILL.md). When solved: `status: in_progress`, remove `statusReason` and `blockerType`. Never set `cancelled` yourself; only with the user's OK.
36
+
37
+ ## Verify and fix
38
+
39
+ **VERIFY** — pick the checks that fit this change and run them with the project's own tools (RODO runs nothing itself):
40
+ - build, typecheck, lint;
41
+ - unit, integration, e2e;
42
+ - architecture rules, security checks;
43
+ - acceptance (the card's `test` command via `rodo test`, or the acceptance criteria checked one by one);
44
+ - documentation consistency (below).
45
+
46
+ Write the result in Progress notes as `- Verified: <check> ✓/✗, …`.
47
+
48
+ **Documentation Consistency** (part of VERIFY):
49
+ - Check that every file listed under Documentation Impact was updated.
50
+ - Check that the CURRENT docs and the active cycle docs still describe the code.
51
+ - If something does not match: FIX, then VERIFY again.
52
+ - If a current doc contradicts a `MEMORY.md` decision, stop and ask the user (as small-change.md already says).
53
+
54
+ **REVIEW (optional; recommended for MAJOR)** — an independent AI session or model reads the change and checks:
55
+ - requirement coverage; acceptance criteria;
56
+ - regressions; architecture fit;
57
+ - missing tests; maintainability;
58
+ - unintended changes.
59
+
60
+ A review never replaces tests. Its findings go back to FIX.
61
+
62
+ **FIX loop** — FIX → VERIFY until everything passes (→ DONE) or the stuck rule applies: the **same failure twice** (same failure signature, or the same error at the same place) → `status: blocked` with `blockerType: test` or `technical` (SKILL.md "Stuck rule"). Different failures do not count.
63
+
64
+ ## Clean before you close
65
+
66
+ When the steps are done and verified, follow `clean.md` (and `review.md` for MAJOR), then tick `Cleanup done`.
67
+
68
+ ## End of every session
69
+
70
+ Follow "Session close" in SKILL.md. Always finish with `rodo sync`.
71
+
72
+ ## Close (all steps ticked)
73
+
74
+ The folder does not move.
75
+ 1. Card: `status: done`.
76
+ 2. `MAP.md`: add the cycle under `## انجام‌شده` (one line, link to simple.md or the card folder) and update "الان کجاییم".
77
+ 3. `MEMORY.md`: append each `## Decisions` line of spec.md as `- YYYY-MM-DD — <decision>. چون <reason>. (cycle NNN)`.
78
+ 4. Commit the MAP and MEMORY change. Later changes to this behaviour need a new card; changes that do not touch behaviour go to `small-change.md`.
79
+
80
+ ## Rules
81
+
82
+ - Never tick an approval item or change its text. Only a person can approve, in the web app. Never answer a `## Decision:` yourself.
83
+ - Never edit `id:` lines or `<!-- rodo:... -->` comments.
84
+ - Do not change `members`, `startAt`, `dueAt`, `priority` unless the user asks: the team owns them.
85
+ - Never delete files you did not create in this card without asking.
86
+ - Keep changes inside the steps. Unrelated fixes go to `small-change.md` or a new card.
@@ -8,7 +8,7 @@ Goal: create `rodo/<project>/` so the project can follow the method, without cha
8
8
 
9
9
  1. **Read, do not write.** Read README, existing docs, package files, folder structure, git log (last ~100 commits), and TODO/FIXME comments. Skim code by folder; do not read every file.
10
10
  2. **Create the skeleton**: run `rodo init --method-only --project <slug>` (if the CLI is installed), or copy `project-template/` into `rodo/<slug>/` yourself. It never overwrites an existing file. Set `language` in `project.json` if the user wants other languages.
11
- 3. **brief.md and architecture.md**: describe what exists today. Mark guesses with `(حدس)` so the user can confirm. Then write `project/project-overview.md` and `project/how-it-works.md` the same way (`project-docs.md`), ticking only parts that already work.
11
+ 3. **brief.md and architecture.md**: describe what exists today. Mark guesses with `(حدس)` so the user can confirm.
12
12
  4. **MEMORY.md**: decisions visible in the code or docs (framework, database, auth method...). One line each with the reason if known, else `چون: نامعلوم`.
13
13
  5. **Finished work → done cycles.** For each finished feature a user can see, create `rodo/<project>/cycles/NNN-slug/` with:
14
14
  - `simple.md` whose FIRST line is `<!-- rodo:onboarded -->`, then 3-6 simple lines;
@@ -24,7 +24,6 @@ Yes, for every new feature, change or improvement, and for every change to behav
24
24
  - List the current documents this change will make wrong or must extend.
25
25
  - Use `MAP.md`, `MEMORY.md`, `project/*`, the active cycle, the card's refs, and a code search (Graphify or similar, if available in the project).
26
26
  - Write the list in `spec.md` `## Documentation Impact` (`- <path> — <what changes>`, or `- none — <why>`).
27
- - **Project docs first:** if a new feature or major change does not fit `project/project-overview.md` or `project/how-it-works.md`, update them before BUILD, following `project-docs.md`. If they are still templates, fill them first.
28
27
  - QUICK: do this in your head and fix any doc you made wrong (small-change.md).
29
28
 
30
29
  ## Classify
@@ -0,0 +1,30 @@
1
+ # Review checklists
2
+
3
+ Use before closing a MAJOR card (recommended) or whenever the user asks for a review. A review reads the diff; it never replaces tests. Report findings in plain words, most serious first, and fix them (FIX → VERIFY).
4
+
5
+ ## 1. Simplify (always)
6
+
7
+ - Anything duplicated, over-built, or unused in the diff?
8
+ - Could a simpler existing function or library call replace new code?
9
+ - Follow `clean.md`.
10
+
11
+ ## 2. Correctness
12
+
13
+ - Does each R-rule of `spec.md` have code and, if tests are used, a test?
14
+ - Edge cases: empty input, missing value, very large input, two things happening at once.
15
+ - Errors: are they handled or reported, never swallowed?
16
+
17
+ ## 3. Security (quick pass)
18
+
19
+ - No secrets in code, config, logs, cards or commit messages.
20
+ - Input from outside (forms, URLs, files, env) is checked before use; no string-built SQL, shell commands or HTML.
21
+ - Access: every new route or action checks who is calling.
22
+ - New dependencies: known, maintained, and necessary.
23
+
24
+ ## 4. Plain words
25
+
26
+ - Does the card's opening text (what the user gets) and `simple.md` still describe what the code now does? Run `rodo explain <card>` and read it as if you were not a developer.
27
+
28
+ ## Optional external skills
29
+
30
+ If the project or the user's tools already provide them, you may use `simplify`, `code-review`, `security-review` or Graphify (code map). Never install them yourself; RODO works without them. Treat their output as advice and verify it before acting.
@@ -11,7 +11,7 @@ scope:
11
11
  forbidden: []
12
12
  ---
13
13
 
14
- {{دو تا پنج خط ساده: کاربر چه چیزی می‌گیرد. جزئیات در spec.md است.}}
14
+ {{دو تا پنج خط ساده و بدون اصطلاح فنی: کاربر چه چیزی می‌گیرد. دستور rodo explain همین را به کاربر نشان می‌دهد. جزئیات در spec.md است.}}
15
15
 
16
16
  ## Progress notes
17
17
 
@@ -30,4 +30,5 @@ scope:
30
30
  <!-- - [ ] تأیید کاربر: simple.md و سناریوها [approval] — add only when the user asked for a human approval (D5) -->
31
31
  - [ ] {{قدم ۱: کار مشخص + نشانهٔ انجام شدن}}
32
32
  - [ ] {{قدم ۲}}
33
+ - [ ] تمیزکاری انجام شد (clean.md)
33
34
  - [ ] تست پذیرش پاس شد
@@ -1,32 +1,33 @@
1
- ---
2
- schemaVersion: 2
3
- title: "{{short title}}"
4
- status: todo
5
- # statusReason: ... # only when status is blocked or cancelled: the reason in plain words
6
- priority: medium
7
- # workflow: standard # optional: quick | standard | major; local only, never sent to the board
8
- members: []
9
- scope:
10
- allowed: []
11
- forbidden: []
12
- ---
13
-
14
- {{two to five plain lines: what the user gets. Details are in spec.md.}}
15
-
16
- ## Progress notes
17
-
18
- - Updated: {{YYYY-MM-DD}}
19
- - Done: not started yet
20
- - Remaining: all steps
21
-
22
- ## Refs
23
-
24
- - [Plain explanation](/rodo/{{project}}/cycles/{{NNN-slug}}/simple.md)
25
- - [Technical spec](/rodo/{{project}}/cycles/{{NNN-slug}}/spec.md)
26
- <!-- [Acceptance scenarios](/rodo/{{project}}/cycles/{{NNN-slug}}/acceptance.feature) — uncomment only after that file exists -->
27
-
28
- ## Checklist: Steps
29
-
30
- <!-- - [ ] User approval: simple.md and scenarios [approval] — add only when the user asked for a human approval -->
31
- - [ ] {{step 1: a concrete task + how you know it is done}}
32
- - [ ] {{step 2}}
1
+ ---
2
+ schemaVersion: 2
3
+ title: "{{short title}}"
4
+ status: todo
5
+ # statusReason: ... # only when status is blocked or cancelled: the reason in plain words
6
+ priority: medium
7
+ # workflow: standard # optional: quick | standard | major; local only, never sent to the board
8
+ members: []
9
+ scope:
10
+ allowed: []
11
+ forbidden: []
12
+ ---
13
+
14
+ {{two to five plain lines: what the user gets, with no technical terms. `rodo explain` shows exactly this to the user. Details are in spec.md.}}
15
+
16
+ ## Progress notes
17
+
18
+ - Updated: {{YYYY-MM-DD}}
19
+ - Done: not started yet
20
+ - Remaining: all steps
21
+
22
+ ## Refs
23
+
24
+ - [Plain explanation](/rodo/{{project}}/cycles/{{NNN-slug}}/simple.md)
25
+ - [Technical spec](/rodo/{{project}}/cycles/{{NNN-slug}}/spec.md)
26
+ <!-- [Acceptance scenarios](/rodo/{{project}}/cycles/{{NNN-slug}}/acceptance.feature) — uncomment only after that file exists -->
27
+
28
+ ## Checklist: Steps
29
+
30
+ <!-- - [ ] User approval: simple.md and scenarios [approval] — add only when the user asked for a human approval -->
31
+ - [ ] {{step 1: a concrete task + how you know it is done}}
32
+ - [ ] {{step 2}}
33
+ - [ ] Cleanup done (clean.md)
@@ -1 +1 @@
1
- 2.3.0
1
+ 2.4.0
@@ -2,15 +2,11 @@
2
2
 
3
3
  Kit 2.1+ is installed and updated by `rodo init` (the 2.0 scripts `rodo-install.cjs` and `rodo-update.cjs` are replaced by it). Do the steps for every version you skipped.
4
4
 
5
- ## 2.2.1 → 2.3.0 (two plain project docs)
5
+ ## 2.2.1 → 2.4.0 (clean code, review, plain words)
6
6
 
7
- What changes:
8
- - **New files.** `project/project-overview.md` (for a client, parts with ticks) and `project/how-it-works.md` (for a beginner). The writing rules are in the new skill file `project-docs.md`.
9
- - **Planner and coder.** The planner updates these files before work starts when a new feature or major change does not fit them; the coder makes them true and ticks the part when a card closes.
10
- - **`rodo validate`** warns `PROJECT_DOC_TOO_LONG` when one of them passes its word limit. It never blocks anything.
11
- - **`rodo init`** uses the project's own language when it adds missing files to an existing project.
12
-
13
- Steps: run `rodo init`. It adds the two files without touching existing ones. The next planner session fills them in. `RULES.md` is never replaced: compare it with the kit's new one (new section on the two docs). Commit: `git commit -am "chore: RODO kit 2.3.0"`.
7
+ - New kit files: `clean.md` (clean-code formulas) and `review.md` (review checklists). `rodo init` adds them; nothing is removed.
8
+ - Card templates: plain-words hint on the opening text and a `Cleanup done` checklist step. Existing cards need no change.
9
+ - New command: `rodo explain [card]` prints a card in plain words.
14
10
 
15
11
  ## 2.1.0 → 2.2.0 (English templates, rodo new, safer sync)
16
12
 
@@ -1,5 +1,5 @@
1
1
  <!-- rodo:start -->
2
- This repo uses the RODO method (kit 2.3.0). Before any work: read `.agents/skills/rodo/SKILL.md`,
2
+ This repo uses the RODO method (kit 2.4.0). Before any work: read `.agents/skills/rodo/SKILL.md`,
3
3
  run `rodo sync`, then `rodo resume`. At the end of every session run `rodo sync`.
4
4
  RODO projects: see `rodo/*/project.json`. Never write secrets into rodo files. Never create boards.
5
5
  Never run `git pull`, `git fetch`, `git merge` or `git rebase`. If the repo is behind, tell the user once.
@@ -1,11 +1,16 @@
1
1
  {
2
- "version": "2.3.0",
2
+ "version": "2.4.0",
3
3
  "files": [
4
4
  {
5
5
  "src": ".agents/skills/rodo/SKILL.md",
6
6
  "dest": ".agents/skills/rodo/SKILL.md",
7
7
  "owner": "kit"
8
8
  },
9
+ {
10
+ "src": ".agents/skills/rodo/clean.md",
11
+ "dest": ".agents/skills/rodo/clean.md",
12
+ "owner": "kit"
13
+ },
9
14
  {
10
15
  "src": ".agents/skills/rodo/coder.md",
11
16
  "dest": ".agents/skills/rodo/coder.md",
@@ -22,8 +27,8 @@
22
27
  "owner": "kit"
23
28
  },
24
29
  {
25
- "src": ".agents/skills/rodo/project-docs.md",
26
- "dest": ".agents/skills/rodo/project-docs.md",
30
+ "src": ".agents/skills/rodo/review.md",
31
+ "dest": ".agents/skills/rodo/review.md",
27
32
  "owner": "kit"
28
33
  },
29
34
  {
@@ -186,16 +191,6 @@
186
191
  "src": "project-template/project/glossary.md",
187
192
  "dest": "project/glossary.md",
188
193
  "owner": "project"
189
- },
190
- {
191
- "src": "project-template/project/how-it-works.md",
192
- "dest": "project/how-it-works.md",
193
- "owner": "project"
194
- },
195
- {
196
- "src": "project-template/project/project-overview.md",
197
- "dest": "project/project-overview.md",
198
- "owner": "project"
199
194
  }
200
195
  ],
201
196
  "projectTemplateEn": [
@@ -238,16 +233,6 @@
238
233
  "src": "project-template-en/project/glossary.md",
239
234
  "dest": "project/glossary.md",
240
235
  "owner": "project"
241
- },
242
- {
243
- "src": "project-template-en/project/how-it-works.md",
244
- "dest": "project/how-it-works.md",
245
- "owner": "project"
246
- },
247
- {
248
- "src": "project-template-en/project/project-overview.md",
249
- "dest": "project/project-overview.md",
250
- "owner": "project"
251
236
  }
252
237
  ]
253
238
  }
@@ -1,4 +1,4 @@
1
- # روش RODO: راهنمای آدم‌ها (نسخهٔ کیت ۲.۳.۰)
1
+ # روش RODO: راهنمای آدم‌ها (نسخهٔ کیت ۲.۲.۱)
2
2
 
3
3
  > این فایل برای شماست، نه برای AI. AI همین قواعد را از `.agents/skills/rodo/` می‌خواند.
4
4
  > اگر فقط ۲ دقیقه وقت دارید، «شروع سریع» و بخش ۱، ۳ و ۴ را بخوانید.
@@ -54,9 +54,7 @@ rodo/
54
54
  ├── project/
55
55
  │ ├── brief.md پروژه به زبان ساده
56
56
  │ ├── architecture.md بخش‌ها و فناوری‌ها، و چرا
57
- │ ├── glossary.md واژه‌های پروژه و معنی‌شان
58
- │ ├── project-overview.md طرح کلی برای کارفرما: بخش‌ها با تیک انجام‌شده‌ها
59
- │ └── how-it-works.md هر بخش چطور کار می‌کند، به زبان ساده برای مبتدی
57
+ │ └── glossary.md واژه‌های پروژه و معنی‌شان
60
58
  ├── inbox/ کارت‌هایی که در وب ساخته شده‌اند (در Git نیست)
61
59
  └── cycles/
62
60
  └── 002-discount-code/ یک کارت و سندهایش (هیچ‌وقت جابه‌جا نمی‌شود)
@@ -234,12 +232,6 @@ AI قبل از هر تغییر این سؤال را از خودش می‌پرس
234
232
  - سندهای کارهای تمام‌شده یا لغوشده هرگز بازنویسی نمی‌شوند؛ تصمیم تازه در کارت تازه و در `MEMORY.md` نوشته می‌شود.
235
233
  - RODO خودش چیزی را اجرا نمی‌کند؛ فقط می‌گوید کدام بررسی‌ها برای این کار مناسب است و ابزارهای خود پروژه آن‌ها را اجرا می‌کنند.
236
234
 
237
- ## دو سند سادهٔ پروژه
238
- - `project-overview.md` طرح کلی برای کارفرماست: پروژه چیست، چه بخش‌هایی دارد و کنار بخش‌های تمام‌شده تیک خورده است.
239
- - `how-it-works.md` برای شماست: پروژه از چه ساخته شده، هر بخش چطور کار می‌کند و کجای کار را راحت می‌کند.
240
- - اگر قابلیت تازه یا تغییر بزرگ با این دو سند نخواند، AI پیش از شروع کار آن‌ها را به‌روز می‌کند و بخش تازه بدون تیک اضافه می‌شود. موقع بستن کارت، متن با واقعیت یکی و تیک زده می‌شود.
241
- - این دو سند هیچ‌وقت از یکی دو صفحه بیشتر نمی‌شوند. `rodo validate` اگر طولانی شوند هشدار می‌دهد.
242
-
243
235
  ---
244
236
 
245
237
  ## ۹. مثال کوتاه: فروشگاه گل مریم
@@ -1,4 +1,4 @@
1
- # The RODO method: guide for people (kit 2.3.0)
1
+ # The RODO method: guide for people (kit 2.4.0)
2
2
 
3
3
  > This file is for you, not for the AI. The AI reads the same rules from `.agents/skills/rodo/`.
4
4
 
@@ -38,7 +38,7 @@ rodo/
38
38
  ├── MEMORY.md important decisions, one line each with "because"
39
39
  ├── CHANGELOG.md small changes without a card, one line each
40
40
  ├── RULES.md this file
41
- ├── project/ brief, architecture, glossary, project-overview, how-it-works (always current)
41
+ ├── project/ brief, architecture, glossary (always current)
42
42
  └── cycles/NNN-slug/ one folder per card: card.rodo.md, simple.md, spec.md, design.md, acceptance.feature
43
43
  ```
44
44
 
@@ -63,14 +63,7 @@ For every new feature, change or improvement, and for every change to behaviour
63
63
  - With a board, card files live on the board and arrive through `rodo sync`; `rodo init` adds them to `.gitignore`.
64
64
  - `rodo sync` never loses your text: when the board wins a conflict, your file is saved in `rodo/<project>/.rodo/conflicts/`.
65
65
 
66
- ## 7. The two plain project docs
67
-
68
- - `project-overview.md` is the plan for a client: what the project is, its parts, and a tick beside the finished ones.
69
- - `how-it-works.md` is for you: what the project is built from, how each part works and what it makes easier.
70
- - If a new feature or major change does not fit them, the AI updates them before work starts and a new part goes in unticked. When the card closes, the text is made true and the part is ticked.
71
- - They never grow past one or two pages; `rodo validate` warns if they do.
72
-
73
- ## 8. Never
66
+ ## 7. Never
74
67
 
75
68
  - No secrets (passwords, keys, tokens) in any file under `rodo/`, in cards or in commit messages.
76
69
  - The AI never deletes files without your OK, never creates boards, and never answers decisions or ticks approvals.
package/package.json CHANGED
@@ -1,50 +1,49 @@
1
- {
2
- "name": "rodo-spec",
3
- "version": "2.3.0",
4
- "description": "RODO method CLI — install the kit, manage cards, sync with the board",
5
- "license": "MIT",
6
- "type": "module",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/alishafiee1/rodo-spec.git",
10
- "directory": "packages/cli"
11
- },
12
- "homepage": "https://github.com/alishafiee1/rodo-spec#readme",
13
- "keywords": [
14
- "rodo",
15
- "rodo-spec",
16
- "kanban",
17
- "cli",
18
- "spec",
19
- "ai-agent"
20
- ],
21
- "bin": {
22
- "rodo": "dist/index.js"
23
- },
24
- "files": [
25
- "dist",
26
- "README.md",
27
- "LICENSE"
28
- ],
29
- "engines": {
30
- "node": ">=20"
31
- },
32
- "scripts": {
33
- "build": "tsup",
34
- "prepack": "pnpm run build",
35
- "test": "vitest run",
36
- "typecheck": "tsc --noEmit"
37
- },
38
- "devDependencies": {
39
- "@rodo/card-format": "workspace:*",
40
- "@rodo/protocol": "workspace:*",
41
- "@rodo/rodo-kit": "workspace:*",
42
- "@types/node": "^22.0.0",
43
- "commander": "^12.1.0",
44
- "picocolors": "^1.1.1",
45
- "tsup": "^8.3.5",
46
- "typescript": "^5.7.2",
47
- "vitest": "^2.1.8",
48
- "yaml": "^2.7.0"
49
- }
50
- }
1
+ {
2
+ "name": "rodo-spec",
3
+ "version": "2.4.0",
4
+ "description": "RODO method CLI — install the kit, manage cards, sync with the board",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/alishafiee1/rodo-spec.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "homepage": "https://github.com/alishafiee1/rodo-spec#readme",
13
+ "keywords": [
14
+ "rodo",
15
+ "rodo-spec",
16
+ "kanban",
17
+ "cli",
18
+ "spec",
19
+ "ai-agent"
20
+ ],
21
+ "bin": {
22
+ "rodo": "dist/index.js"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "engines": {
30
+ "node": ">=20"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "commander": "^12.1.0",
35
+ "picocolors": "^1.1.1",
36
+ "tsup": "^8.3.5",
37
+ "typescript": "^5.7.2",
38
+ "vitest": "^2.1.8",
39
+ "yaml": "^2.7.0",
40
+ "@rodo/card-format": "2.4.0",
41
+ "@rodo/protocol": "2.4.0",
42
+ "@rodo/rodo-kit": "2.4.0"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "test": "vitest run",
47
+ "typecheck": "tsc --noEmit"
48
+ }
49
+ }
@@ -1,41 +0,0 @@
1
- > یادداشت فارسی: قانون نگارش دو سند سادهٔ پروژه: `project-overview.md` برای کارفرما و `how-it-works.md` برای یک مبتدی. این سندها همیشه کوتاه می‌مانند و پیش از شروع کار با طرح تازه هماهنگ می‌شوند. همهٔ مسیرها نسبت به `rodo/<project>/` هستند.
2
-
3
- # Project docs playbook
4
-
5
- Two short documents explain the whole project to people. Write them in the language of `language.simple` in `project.json`. They do not replace `brief.md` or `architecture.md`; those stay as they are.
6
-
7
- | File | Reader | Answers | Limit |
8
- |---|---|---|---|
9
- | `project/project-overview.md` | the client | what is this project, what parts does it have, which are finished | 500 words |
10
- | `project/how-it-works.md` | the owner, a beginner who builds with AI | what is it built from, how does each part work, what does each part make easier | 900 words |
11
-
12
- ## project-overview.md
13
-
14
- - Write it like a plan you hand to a client. Short sentences, no jargon.
15
- - **No technology names** (no framework, database or service names). Describe what the user gets.
16
- - Sections: what this project is, goals, parts of the project.
17
- - One line per part: `- [ ] **Name**: two or three sentences`. Name parts the way a client would, not after cycles or folders.
18
- - Planned parts stay visible, unticked. Cancelled parts are removed.
19
- - `[x]` means the part is finished and works. Never tick a part that is only planned, half done or untested when it has tests.
20
-
21
- ## how-it-works.md
22
-
23
- - Write like an experienced engineer explaining to an intern, in plain words. Technology names are welcome; explain each one the first time it appears.
24
- - Start with the big picture: a few sentences on what happens when a user does something, and which part talks to which.
25
- - One `##` section per part or piece of infrastructure, each answering: what is it and how does it work, and what does it make easier (what would be hard without it).
26
- - Use one example from this project in place of a general definition.
27
- - Depth follows importance: a main part may get a paragraph, a small helper one or two sentences.
28
- - Never write secrets, passwords, keys or server addresses that are not public.
29
-
30
- ## When to update them
31
-
32
- 1. **Planning (before work starts).** In Documentation Impact, ask: does this new feature or major change fit what these two files say? If not, update them before BUILD. A new part goes in the overview unticked. In `how-it-works.md` mark a part that does not exist yet with `(planned)`. List both files in `spec.md` `## Documentation Impact`.
33
- 2. **Closing a card.** Make the text match what was really built, remove `(planned)`, and tick the part in the overview if all of it is finished. A part that needs more cards stays unticked.
34
- 3. **QUICK changes** do not touch these files, unless a sentence in them became false.
35
- 4. **Empty or template files** (they still contain `{{`): the next planner fills them from the code and the current docs before planning its card. Mark anything you could not confirm with `(guess)`, or `(حدس)` in Persian.
36
-
37
- ## Staying short
38
-
39
- - Never let a file grow past its limit. `rodo validate` warns with `PROJECT_DOC_TOO_LONG`; it never blocks anything.
40
- - To add something when a file is full, shorten or merge what is there. Do not add a second file or an appendix.
41
- - These files describe the project as a whole. History belongs in `MAP.md`, decisions in `MEMORY.md`, details in `architecture.md` and `spec.md`.
@@ -1,13 +0,0 @@
1
- # {{نام پروژه}}: چطور کار می‌کند
2
-
3
- <!-- برای یک مبتدی. مثل یک متخصص باتجربه که به کارآموز توضیح می‌دهد. حداکثر ۹۰۰ کلمه. طول هر بخش به اهمیت آن بستگی دارد. قانون کامل: .agents/skills/rodo/project-docs.md -->
4
-
5
- ## نمای کلی
6
-
7
- {{چند جمله: وقتی کاربر کاری می‌کند، درخواست از کدام بخش‌ها می‌گذرد و کدام بخش با کدام حرف می‌زند}}
8
-
9
- ## {{نام بخش یا زیرساخت، مثلاً پایگاه داده}}
10
-
11
- **چیست و چطور کار می‌کند:** {{به زبان ساده، با یک مثال از همین پروژه}}
12
-
13
- **کجای کار را راحت می‌کند:** {{اگر این بخش نبود چه کاری سخت می‌شد}}
@@ -1,15 +0,0 @@
1
- # {{نام پروژه}}: طرح کلی
2
-
3
- <!-- برای کارفرما. زبان ساده، بدون نام فناوری، حداکثر ۵۰۰ کلمه. تیک فقط وقتی زده می‌شود که آن بخش واقعاً تمام شده باشد. قانون کامل: .agents/skills/rodo/project-docs.md -->
4
-
5
- ## این پروژه چیست
6
-
7
- {{دو یا سه جمله: این پروژه چیست، برای چه کسی است و چه مشکلی را حل می‌کند}}
8
-
9
- ## هدف‌ها
10
-
11
- - {{چیزی که با تمام شدن پروژه به دست می‌آید}}
12
-
13
- ## بخش‌های پروژه
14
-
15
- - [ ] **{{نام بخش}}**: {{دو یا سه جمله دربارهٔ اینکه این بخش برای کاربر چه کاری می‌کند}}
@@ -1,13 +0,0 @@
1
- # {{name}}: how it works
2
-
3
- <!-- For a beginner. Written like an experienced engineer explaining to an intern. At most 900 words. Each part is as long as its importance needs. Full rules: .agents/skills/rodo/project-docs.md -->
4
-
5
- ## The big picture
6
-
7
- {{a few sentences: when a user does something, which parts the request passes through and which part talks to which}}
8
-
9
- ## {{part or infrastructure name, for example the database}}
10
-
11
- **What it is and how it works:** {{in plain words, with an example from this project}}
12
-
13
- **What it makes easier:** {{what would be hard without it}}
@@ -1,15 +0,0 @@
1
- # {{name}}: overview
2
-
3
- <!-- For the client. Plain words, no technology names, at most 500 words. Tick a part only when it is really finished. Full rules: .agents/skills/rodo/project-docs.md -->
4
-
5
- ## What this project is
6
-
7
- {{two or three sentences: what it is, who it is for and what problem it solves}}
8
-
9
- ## Goals
10
-
11
- - {{what the project delivers when it is finished}}
12
-
13
- ## Parts of the project
14
-
15
- - [ ] **{{part name}}**: {{two or three sentences on what this part does for the user}}