@kiwidata/grimoire 0.1.4 → 0.1.5

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 (81) hide show
  1. package/README.md +79 -58
  2. package/dist/cli/index.js +5 -7
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/core/check.d.ts.map +1 -1
  5. package/dist/core/check.js +54 -11
  6. package/dist/core/check.js.map +1 -1
  7. package/dist/core/doc-style.d.ts.map +1 -1
  8. package/dist/core/doc-style.js +76 -0
  9. package/dist/core/doc-style.js.map +1 -1
  10. package/dist/core/docs.js +96 -70
  11. package/dist/core/docs.js.map +1 -1
  12. package/dist/core/health.d.ts +6 -0
  13. package/dist/core/health.d.ts.map +1 -1
  14. package/dist/core/health.js +78 -19
  15. package/dist/core/health.js.map +1 -1
  16. package/dist/core/hooks.js +11 -5
  17. package/dist/core/hooks.js.map +1 -1
  18. package/dist/core/risk-register.d.ts +17 -0
  19. package/dist/core/risk-register.d.ts.map +1 -0
  20. package/dist/core/risk-register.js +73 -0
  21. package/dist/core/risk-register.js.map +1 -0
  22. package/dist/core/shared-setup.d.ts.map +1 -1
  23. package/dist/core/shared-setup.js +5 -4
  24. package/dist/core/shared-setup.js.map +1 -1
  25. package/dist/core/trace.d.ts.map +1 -1
  26. package/dist/core/trace.js +37 -35
  27. package/dist/core/trace.js.map +1 -1
  28. package/dist/index.d.ts +0 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +0 -3
  31. package/dist/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/skills/grimoire-apply/SKILL.md +35 -39
  34. package/skills/grimoire-commit/SKILL.md +1 -1
  35. package/skills/grimoire-design/SKILL.md +3 -3
  36. package/skills/grimoire-discover/SKILL.md +77 -110
  37. package/skills/grimoire-draft/SKILL.md +51 -18
  38. package/skills/grimoire-plan/SKILL.md +62 -32
  39. package/skills/grimoire-pr/SKILL.md +7 -8
  40. package/skills/grimoire-pr-review/SKILL.md +1 -1
  41. package/skills/grimoire-refactor/SKILL.md +2 -2
  42. package/skills/grimoire-review/SKILL.md +12 -0
  43. package/skills/grimoire-verify/SKILL.md +7 -7
  44. package/skills/grimoire-vuln-remediate/SKILL.md +107 -0
  45. package/skills/grimoire-vuln-triage/SKILL.md +109 -0
  46. package/skills/references/code-quality.md +41 -9
  47. package/skills/references/container-scan-triage.md +102 -0
  48. package/skills/references/dependency-vuln-triage.md +236 -0
  49. package/skills/references/principles.md +82 -0
  50. package/skills/references/refactor-scan-categories.md +2 -2
  51. package/skills/references/review-personas.md +4 -3
  52. package/skills/references/testing-contracts.md +1 -1
  53. package/templates/accepted-risks.yml +47 -0
  54. package/templates/constraints.md +25 -0
  55. package/dist/commands/archive.d.ts +0 -3
  56. package/dist/commands/archive.d.ts.map +0 -1
  57. package/dist/commands/archive.js +0 -22
  58. package/dist/commands/archive.js.map +0 -1
  59. package/dist/commands/log.d.ts +0 -3
  60. package/dist/commands/log.d.ts.map +0 -1
  61. package/dist/commands/log.js +0 -15
  62. package/dist/commands/log.js.map +0 -1
  63. package/dist/commands/map.d.ts +0 -3
  64. package/dist/commands/map.d.ts.map +0 -1
  65. package/dist/commands/map.js +0 -16
  66. package/dist/commands/map.js.map +0 -1
  67. package/dist/core/archive.d.ts +0 -9
  68. package/dist/core/archive.d.ts.map +0 -1
  69. package/dist/core/archive.js +0 -81
  70. package/dist/core/archive.js.map +0 -1
  71. package/dist/core/log.d.ts +0 -8
  72. package/dist/core/log.d.ts.map +0 -1
  73. package/dist/core/log.js +0 -140
  74. package/dist/core/log.js.map +0 -1
  75. package/dist/core/map.d.ts +0 -22
  76. package/dist/core/map.d.ts.map +0 -1
  77. package/dist/core/map.js +0 -365
  78. package/dist/core/map.js.map +0 -1
  79. package/templates/dupignore +0 -93
  80. package/templates/mapignore +0 -58
  81. package/templates/mapkeys +0 -65
@@ -0,0 +1,25 @@
1
+ # Constraints
2
+
3
+ > Invariants that must always hold. These are **not** behaviors (no external actor,
4
+ > not observable in a scenario) — they are guarantees. Anything that failed the
5
+ > feature-file admission test in `grimoire-draft` because it is a security control,
6
+ > NFR, performance budget, observability guarantee, or compliance rule lives here,
7
+ > **not** in a `.feature`.
8
+ >
9
+ > Each constraint is verified by a `unit-invariant` test (created at plan/apply),
10
+ > never by a Gherkin scenario. Keep this register narrow: assert, justify, point to
11
+ > the proof. Don't let it grow into an issue tracker — open work belongs in your
12
+ > tracker, not here.
13
+
14
+ | Constraint (assertion) | Rationale | How verified | Links |
15
+ |------------------------|-----------|--------------|-------|
16
+ | _e.g._ Log output never contains PII or secrets | Confidential data must not leak to logs/stdout | `tests/test_log_scrubbing.py::test_pii_redacted` | [ADR-0008](.grimoire/decisions/0008-...) |
17
+ | _e.g._ Every request is isolated to its tenant | Multi-tenant data separation | `tests/test_tenant_isolation.py` | — |
18
+
19
+ <!--
20
+ Add one row per constraint. Guidance:
21
+ - Assertion: a flat "X always holds" statement. No Given/When/Then.
22
+ - Rationale: why it matters, in one line.
23
+ - How verified: the exact test id that proves it. If none yet, write "TODO: unit-invariant test" — the plan stage will create it.
24
+ - Links: the MADR that decided it (don't restate the decision here — DRY).
25
+ -->
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const archiveCommand: Command;
3
- //# sourceMappingURL=archive.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,cAAc,SAgBvB,CAAC"}
@@ -1,22 +0,0 @@
1
- import { Command } from "commander";
2
- import { archiveChange, ArchiveError } from "../core/archive.js";
3
- import chalk from "chalk";
4
- export const archiveCommand = new Command("archive")
5
- .description("Archive a completed change")
6
- .argument("<change-id>", "Change to archive")
7
- .option("-y, --yes", "Skip confirmation prompt")
8
- .action(async (changeId, options) => {
9
- try {
10
- await archiveChange(changeId, {
11
- yes: options.yes ?? false,
12
- });
13
- }
14
- catch (err) {
15
- if (err instanceof ArchiveError) {
16
- console.error(chalk.red(err.message));
17
- process.exit(1);
18
- }
19
- throw err;
20
- }
21
- });
22
- //# sourceMappingURL=archive.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;KACjD,WAAW,CAAC,4BAA4B,CAAC;KACzC,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC;KAC5C,MAAM,CAAC,WAAW,EAAE,0BAA0B,CAAC;KAC/C,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,OAAO,EAAE,EAAE;IAC1C,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,QAAQ,EAAE;YAC5B,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;SAC1B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const logCommand: Command;
3
- //# sourceMappingURL=log.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,UAAU,SAWnB,CAAC"}
@@ -1,15 +0,0 @@
1
- import { Command } from "commander";
2
- import { generateLog } from "../core/log.js";
3
- export const logCommand = new Command("log")
4
- .description("Generate change log from archived grimoire changes")
5
- .option("--from <ref>", "Start date or git tag (inclusive)")
6
- .option("--to <ref>", "End date or git tag (inclusive)")
7
- .option("--json", "Output as JSON")
8
- .action(async (options) => {
9
- await generateLog({
10
- from: options.from,
11
- to: options.to,
12
- json: options.json ?? false,
13
- });
14
- });
15
- //# sourceMappingURL=log.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/commands/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,cAAc,EAAE,mCAAmC,CAAC;KAC3D,MAAM,CAAC,YAAY,EAAE,iCAAiC,CAAC;KACvD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,WAAW,CAAC;QAChB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;KAC5B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare const mapCommand: Command;
3
- //# sourceMappingURL=map.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/commands/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,eAAO,MAAM,UAAU,SAUnB,CAAC"}
@@ -1,16 +0,0 @@
1
- import { Command } from "commander";
2
- import { runMap, McpRequiredError } from "../core/map.js";
3
- export const mapCommand = new Command("map")
4
- .description("Detect drift between conventions files and the current codebase")
5
- .option("--duplicates", "Run jscpd to detect code duplication")
6
- .action(async (options) => {
7
- try {
8
- await runMap({ duplicates: options.duplicates ?? false });
9
- }
10
- catch (e) {
11
- if (e instanceof McpRequiredError)
12
- process.exit(1);
13
- throw e;
14
- }
15
- });
16
- //# sourceMappingURL=map.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/commands/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,iEAAiE,CAAC;KAC9E,MAAM,CAAC,cAAc,EAAE,sCAAsC,CAAC;KAC9D,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,KAAK,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,gBAAgB;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -1,9 +0,0 @@
1
- interface ArchiveOptions {
2
- yes: boolean;
3
- }
4
- export declare class ArchiveError extends Error {
5
- constructor(message: string);
6
- }
7
- export declare function archiveChange(changeId: string, options: ArchiveOptions): Promise<void>;
8
- export {};
9
- //# sourceMappingURL=archive.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/core/archive.ts"],"names":[],"mappings":"AAKA,UAAU,cAAc;IACtB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAwCD,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAiCf"}
@@ -1,81 +0,0 @@
1
- import { readFile, mkdir, cp, rm } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import chalk from "chalk";
4
- import { findProjectRoot, resolveChangePath } from "../utils/paths.js";
5
- export class ArchiveError extends Error {
6
- constructor(message) {
7
- super(message);
8
- this.name = "ArchiveError";
9
- }
10
- }
11
- async function checkPendingTasks(changePath, yes) {
12
- try {
13
- const tasksContent = await readFile(join(changePath, "tasks.md"), "utf-8");
14
- const pending = tasksContent.match(/^- \[ \] .+$/gm) || [];
15
- if (pending.length > 0) {
16
- console.log(chalk.yellow(`Warning: ${pending.length} task(s) still pending.`));
17
- if (!yes)
18
- throw new ArchiveError("Use --yes to archive anyway, or complete tasks first.");
19
- }
20
- }
21
- catch (err) {
22
- if (err instanceof ArchiveError)
23
- throw err;
24
- // No tasks file — ok
25
- }
26
- }
27
- async function getUserConfirmation(changeId) {
28
- const readline = await import("node:readline/promises");
29
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30
- const answer = await rl.question(`Archive change "${changeId}"? (y/N) `);
31
- rl.close();
32
- return answer.toLowerCase() === "y";
33
- }
34
- async function syncArtifactsToBaseline(changePath, root) {
35
- try {
36
- await cp(join(changePath, "features"), join(root, "features"), { recursive: true, force: true });
37
- console.log(` ${chalk.green("synced")} features to baseline`);
38
- }
39
- catch {
40
- // No proposed features
41
- }
42
- try {
43
- // TODO: handle sequential numbering for new decisions
44
- await cp(join(changePath, "decisions"), join(root, ".grimoire", "decisions"), { recursive: true, force: true });
45
- console.log(` ${chalk.green("synced")} decisions to baseline`);
46
- }
47
- catch {
48
- // No proposed decisions
49
- }
50
- }
51
- export async function archiveChange(changeId, options) {
52
- const root = await findProjectRoot();
53
- const changePath = resolveChangePath(root, changeId);
54
- try {
55
- await readFile(join(changePath, "manifest.md"), "utf-8");
56
- }
57
- catch {
58
- throw new ArchiveError(`Change "${changeId}" not found or missing manifest.`);
59
- }
60
- await checkPendingTasks(changePath, options.yes);
61
- if (!options.yes) {
62
- if (!(await getUserConfirmation(changeId))) {
63
- console.log("Cancelled.");
64
- return;
65
- }
66
- }
67
- await syncArtifactsToBaseline(changePath, root);
68
- const date = new Date().toISOString().split("T")[0];
69
- const archiveDir = join(root, ".grimoire", "archive", `${date}-${changeId}`);
70
- await mkdir(archiveDir, { recursive: true });
71
- await cp(join(changePath, "manifest.md"), join(archiveDir, "manifest.md"));
72
- try {
73
- await cp(join(changePath, "tasks.md"), join(archiveDir, "tasks.md"));
74
- }
75
- catch {
76
- // no tasks
77
- }
78
- await rm(changePath, { recursive: true });
79
- console.log(`\n${chalk.green("Archived")} ${changeId} → .grimoire/archive/${date}-${changeId}/`);
80
- }
81
- //# sourceMappingURL=archive.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/core/archive.ts"],"names":[],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- interface LogOptions {
2
- from?: string;
3
- to?: string;
4
- json: boolean;
5
- }
6
- export declare function generateLog(options: LogOptions): Promise<void>;
7
- export {};
8
- //# sourceMappingURL=log.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAMA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;CACf;AA+BD,wBAAsB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCpE"}
package/dist/core/log.js DELETED
@@ -1,140 +0,0 @@
1
- import { readdir, readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import chalk from "chalk";
4
- import { simpleGit } from "simple-git";
5
- import { findProjectRoot } from "../utils/paths.js";
6
- function printLogEntry(entry, currentMonth) {
7
- const month = entry.date.slice(0, 7);
8
- if (month !== currentMonth) {
9
- console.log(chalk.bold.underline(`\n${formatMonth(month)}\n`));
10
- }
11
- console.log(` ${chalk.dim(entry.date)} ${chalk.cyan(entry.changeId)}`);
12
- console.log(` ${entry.summary}`);
13
- if (entry.features.length > 0)
14
- console.log(` ${chalk.dim("Features:")} ${entry.features.join(", ")}`);
15
- if (entry.decisions.length > 0)
16
- console.log(` ${chalk.dim("Decisions:")} ${entry.decisions.join(", ")}`);
17
- if (entry.scenarios.length > 0) {
18
- const display = entry.scenarios.length <= 3
19
- ? entry.scenarios.join(", ")
20
- : `${entry.scenarios.slice(0, 3).join(", ")} +${entry.scenarios.length - 3} more`;
21
- console.log(` ${chalk.dim("Scenarios:")} ${display}`);
22
- }
23
- console.log();
24
- return month;
25
- }
26
- export async function generateLog(options) {
27
- const root = await findProjectRoot();
28
- const archiveDir = join(root, ".grimoire", "archive");
29
- let entries;
30
- try {
31
- entries = await readArchiveEntries(archiveDir);
32
- }
33
- catch {
34
- throw new Error("No archive found. No changes have been archived yet.");
35
- }
36
- if (entries.length === 0) {
37
- console.log(chalk.dim("No archived changes found."));
38
- return;
39
- }
40
- if (options.from || options.to) {
41
- const fromDate = options.from ? await resolveDate(root, options.from) : "";
42
- const toDate = options.to ? await resolveDate(root, options.to) : "9999-99-99";
43
- entries = entries.filter((e) => e.date >= fromDate && e.date <= toDate);
44
- }
45
- entries.sort((a, b) => b.date.localeCompare(a.date));
46
- if (options.json) {
47
- console.log(JSON.stringify(entries, null, 2));
48
- return;
49
- }
50
- console.log(chalk.bold("Grimoire Change Log\n"));
51
- let currentMonth = "";
52
- for (const entry of entries) {
53
- currentMonth = printLogEntry(entry, currentMonth);
54
- }
55
- console.log(chalk.dim(`${entries.length} change(s) total`));
56
- }
57
- async function readArchiveEntries(archiveDir) {
58
- const dirs = await readdir(archiveDir, { withFileTypes: true });
59
- const entries = [];
60
- for (const dir of dirs) {
61
- if (!dir.isDirectory())
62
- continue;
63
- // Directory name format: YYYY-MM-DD-<change-id>
64
- const match = dir.name.match(/^(\d{4}-\d{2}-\d{2})-(.+)$/);
65
- if (!match)
66
- continue;
67
- const [, date, changeId] = match;
68
- const manifestPath = join(archiveDir, dir.name, "manifest.md");
69
- let manifest;
70
- try {
71
- manifest = await readFile(manifestPath, "utf-8");
72
- }
73
- catch {
74
- continue;
75
- }
76
- entries.push({
77
- date,
78
- changeId,
79
- ...parseManifest(manifest),
80
- });
81
- }
82
- return entries;
83
- }
84
- function parseManifest(content) {
85
- // Extract title: # Change: <summary>
86
- const titleMatch = content.match(/^#\s+Change:\s*(.+)$/m);
87
- const summary = titleMatch ? titleMatch[1].trim() : "(no summary)";
88
- // Extract why section
89
- const whyMatch = content.match(/^##\s+Why\s*\n([\s\S]*?)(?=^##|\Z)/m);
90
- const why = whyMatch ? whyMatch[1].trim() : "";
91
- // Extract feature changes
92
- const features = [];
93
- const featurePattern = /\*\*(?:ADDED|MODIFIED|REMOVED)\*\*\s+`([^`]+\.feature)`/g;
94
- let m;
95
- while ((m = featurePattern.exec(content)) !== null) {
96
- features.push(m[1]);
97
- }
98
- // Extract decisions
99
- const decisions = [];
100
- const decisionPattern = /\*\*(?:ADDED|MODIFIED|SUPERSEDED)\*\*\s+`(\d{4}-[^`]+\.md)`/g;
101
- while ((m = decisionPattern.exec(content)) !== null) {
102
- decisions.push(m[1]);
103
- }
104
- // Extract scenarios
105
- const scenarios = [];
106
- const scenarioPattern = /"([^"]+)"/g;
107
- const scenarioSection = content.match(/^##\s+Scenarios\s+(?:Added|Modified)\s*\n([\s\S]*?)(?=^##|\Z)/gm);
108
- if (scenarioSection) {
109
- for (const section of scenarioSection) {
110
- while ((m = scenarioPattern.exec(section)) !== null) {
111
- scenarios.push(m[1]);
112
- }
113
- }
114
- }
115
- return { summary, why, features, decisions, scenarios };
116
- }
117
- async function resolveDate(root, ref) {
118
- if (/^\d{4}-\d{2}-\d{2}$/.test(ref)) {
119
- return ref;
120
- }
121
- // Try as a git tag
122
- try {
123
- const git = simpleGit(root);
124
- const stdout = await git.raw(["log", "-1", "--format=%aI", ref]);
125
- return stdout.trim().split("T")[0];
126
- }
127
- catch {
128
- console.error(chalk.yellow(`Warning: Could not resolve "${ref}" as a git tag or date. Using as-is.`));
129
- return ref;
130
- }
131
- }
132
- function formatMonth(yyyyMm) {
133
- const [year, month] = yyyyMm.split("-");
134
- const months = [
135
- "January", "February", "March", "April", "May", "June",
136
- "July", "August", "September", "October", "November", "December",
137
- ];
138
- return `${months[parseInt(month, 10) - 1]} ${year}`;
139
- }
140
- //# sourceMappingURL=log.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"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"}
@@ -1,22 +0,0 @@
1
- export interface MapOptions {
2
- duplicates: boolean;
3
- }
4
- interface DriftItem {
5
- conventionsFile: string;
6
- path: string;
7
- context: string;
8
- }
9
- export declare class McpRequiredError extends Error {
10
- constructor(message: string);
11
- }
12
- export declare function extractPathRules(content: string, filename: string): DriftItem[];
13
- export declare function runMap(options: MapOptions): Promise<void>;
14
- interface LegacyMapOptions {
15
- json: boolean;
16
- refresh: boolean;
17
- maxDepth: number;
18
- duplicates: boolean;
19
- }
20
- export declare function generateMap(options: LegacyMapOptions): Promise<void>;
21
- export {};
22
- //# sourceMappingURL=map.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/core/map.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,SAAS;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAcD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAuB/E;AA2CD,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB/D;AAyHD,UAAU,gBAAgB;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAgHD,wBAAsB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC1E"}