@imix-js/taproot 0.2.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.
Files changed (124) hide show
  1. package/README.md +88 -0
  2. package/dist/adapters/index.d.ts +20 -0
  3. package/dist/adapters/index.js +452 -0
  4. package/dist/adapters/index.js.map +1 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +40 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/acceptance-check.d.ts +26 -0
  9. package/dist/commands/acceptance-check.js +213 -0
  10. package/dist/commands/acceptance-check.js.map +1 -0
  11. package/dist/commands/check-orphans.d.ts +8 -0
  12. package/dist/commands/check-orphans.js +157 -0
  13. package/dist/commands/check-orphans.js.map +1 -0
  14. package/dist/commands/commithook.d.ts +15 -0
  15. package/dist/commands/commithook.js +389 -0
  16. package/dist/commands/commithook.js.map +1 -0
  17. package/dist/commands/coverage.d.ts +41 -0
  18. package/dist/commands/coverage.js +390 -0
  19. package/dist/commands/coverage.js.map +1 -0
  20. package/dist/commands/dod.d.ts +13 -0
  21. package/dist/commands/dod.js +141 -0
  22. package/dist/commands/dod.js.map +1 -0
  23. package/dist/commands/init.d.ts +14 -0
  24. package/dist/commands/init.js +378 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/link-commits.d.ts +12 -0
  27. package/dist/commands/link-commits.js +126 -0
  28. package/dist/commands/link-commits.js.map +1 -0
  29. package/dist/commands/overview.d.ts +6 -0
  30. package/dist/commands/overview.js +192 -0
  31. package/dist/commands/overview.js.map +1 -0
  32. package/dist/commands/plan.d.ts +23 -0
  33. package/dist/commands/plan.js +167 -0
  34. package/dist/commands/plan.js.map +1 -0
  35. package/dist/commands/sync-check.d.ts +8 -0
  36. package/dist/commands/sync-check.js +118 -0
  37. package/dist/commands/sync-check.js.map +1 -0
  38. package/dist/commands/update.d.ts +7 -0
  39. package/dist/commands/update.js +309 -0
  40. package/dist/commands/update.js.map +1 -0
  41. package/dist/commands/validate-format.d.ts +8 -0
  42. package/dist/commands/validate-format.js +93 -0
  43. package/dist/commands/validate-format.js.map +1 -0
  44. package/dist/commands/validate-structure.d.ts +8 -0
  45. package/dist/commands/validate-structure.js +29 -0
  46. package/dist/commands/validate-structure.js.map +1 -0
  47. package/dist/core/config.d.ts +6 -0
  48. package/dist/core/config.js +86 -0
  49. package/dist/core/config.js.map +1 -0
  50. package/dist/core/configuration.d.ts +7 -0
  51. package/dist/core/configuration.js +112 -0
  52. package/dist/core/configuration.js.map +1 -0
  53. package/dist/core/dod-runner.d.ts +20 -0
  54. package/dist/core/dod-runner.js +233 -0
  55. package/dist/core/dod-runner.js.map +1 -0
  56. package/dist/core/dor-runner.d.ts +18 -0
  57. package/dist/core/dor-runner.js +156 -0
  58. package/dist/core/dor-runner.js.map +1 -0
  59. package/dist/core/fs-walker.d.ts +5 -0
  60. package/dist/core/fs-walker.js +74 -0
  61. package/dist/core/fs-walker.js.map +1 -0
  62. package/dist/core/git.d.ts +24 -0
  63. package/dist/core/git.js +76 -0
  64. package/dist/core/git.js.map +1 -0
  65. package/dist/core/impl-reader.d.ts +8 -0
  66. package/dist/core/impl-reader.js +39 -0
  67. package/dist/core/impl-reader.js.map +1 -0
  68. package/dist/core/language.d.ts +39 -0
  69. package/dist/core/language.js +159 -0
  70. package/dist/core/language.js.map +1 -0
  71. package/dist/core/markdown-parser.d.ts +3 -0
  72. package/dist/core/markdown-parser.js +37 -0
  73. package/dist/core/markdown-parser.js.map +1 -0
  74. package/dist/core/reporter.d.ts +3 -0
  75. package/dist/core/reporter.js +33 -0
  76. package/dist/core/reporter.js.map +1 -0
  77. package/dist/templates/index.d.ts +4 -0
  78. package/dist/templates/index.js +126 -0
  79. package/dist/templates/index.js.map +1 -0
  80. package/dist/validators/format-rules.d.ts +10 -0
  81. package/dist/validators/format-rules.js +238 -0
  82. package/dist/validators/format-rules.js.map +1 -0
  83. package/dist/validators/structure-rules.d.ts +10 -0
  84. package/dist/validators/structure-rules.js +94 -0
  85. package/dist/validators/structure-rules.js.map +1 -0
  86. package/dist/validators/types.d.ts +68 -0
  87. package/dist/validators/types.js +2 -0
  88. package/dist/validators/types.js.map +1 -0
  89. package/docs/agents.md +88 -0
  90. package/docs/architecture.md +53 -0
  91. package/docs/cli.md +226 -0
  92. package/docs/concepts.md +268 -0
  93. package/docs/configuration.md +255 -0
  94. package/docs/demo.svg +111 -0
  95. package/docs/patterns.md +118 -0
  96. package/docs/security.md +95 -0
  97. package/docs/workflows.md +151 -0
  98. package/languages/de.json +20 -0
  99. package/languages/en.json +20 -0
  100. package/languages/es.json +20 -0
  101. package/languages/fr.json +20 -0
  102. package/languages/ja.json +20 -0
  103. package/languages/pt.json +20 -0
  104. package/package.json +54 -0
  105. package/skills/analyse-change.md +101 -0
  106. package/skills/behaviour.md +179 -0
  107. package/skills/bug.md +70 -0
  108. package/skills/commit.md +99 -0
  109. package/skills/decompose.md +101 -0
  110. package/skills/discover.md +392 -0
  111. package/skills/grill-me.md +65 -0
  112. package/skills/guide.md +118 -0
  113. package/skills/implement.md +149 -0
  114. package/skills/ineed.md +147 -0
  115. package/skills/intent.md +104 -0
  116. package/skills/plan.md +63 -0
  117. package/skills/promote.md +69 -0
  118. package/skills/refine.md +78 -0
  119. package/skills/research.md +122 -0
  120. package/skills/review-all.md +92 -0
  121. package/skills/review.md +80 -0
  122. package/skills/status.md +103 -0
  123. package/skills/sweep.md +89 -0
  124. package/skills/trace.md +151 -0
@@ -0,0 +1,238 @@
1
+ import { existsSync } from 'fs';
2
+ import { resolve, dirname } from 'path';
3
+ import { getLocalizedRequiredSections } from '../core/language.js';
4
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
5
+ const STATE_LINE = /^\s*[-*]?\s*\*\*State:\*\*\s*(.+)$/m;
6
+ const DATE_LINE = /^\s*[-*]?\s*\*\*(?:Created|Last reviewed|Last verified):\*\*\s*(.+)$/gm;
7
+ export function checkRequiredSections(doc, markerType, pack) {
8
+ const required = getLocalizedRequiredSections(markerType, pack ?? null);
9
+ return required
10
+ .filter(section => !doc.sections.has(section))
11
+ .map(section => ({
12
+ type: 'error',
13
+ filePath: doc.filePath,
14
+ code: 'MISSING_SECTION',
15
+ message: `Required section "## ${titleCase(section)}" is missing`,
16
+ }));
17
+ }
18
+ export function checkStatusValue(doc, markerType, config) {
19
+ if (!config.validation.requireStatus)
20
+ return [];
21
+ const statusSection = doc.sections.get('status');
22
+ if (!statusSection)
23
+ return []; // Caught by checkRequiredSections
24
+ const match = STATE_LINE.exec(statusSection.rawBody);
25
+ if (!match) {
26
+ return [{
27
+ type: 'error',
28
+ filePath: doc.filePath,
29
+ line: statusSection.startLine,
30
+ code: 'MISSING_STATE_LINE',
31
+ message: 'Status section is missing the "**State:**" line',
32
+ }];
33
+ }
34
+ const value = (match[1] ?? '').trim();
35
+ if (markerType === 'intent' && value === 'deferred') {
36
+ const bodyLines = statusSection.bodyLines;
37
+ const stateLine = bodyLines.findIndex(l => /\*\*State:\*\*/.test(l));
38
+ const lineNumber = statusSection.startLine + (stateLine >= 0 ? stateLine + 1 : 0);
39
+ return [{
40
+ type: 'error',
41
+ filePath: doc.filePath,
42
+ line: lineNumber,
43
+ code: 'INVALID_STATUS_VALUE',
44
+ message: 'deferred is not a valid state for intent.md — use deprecated instead',
45
+ }];
46
+ }
47
+ const allowed = markerType === 'intent' ? config.validation.allowedIntentStates :
48
+ markerType === 'behaviour' ? config.validation.allowedBehaviourStates :
49
+ config.validation.allowedImplStates;
50
+ if (!allowed.includes(value)) {
51
+ const bodyLines = statusSection.bodyLines;
52
+ const stateLine = bodyLines.findIndex(l => /\*\*State:\*\*/.test(l));
53
+ const lineNumber = statusSection.startLine + (stateLine >= 0 ? stateLine + 1 : 0);
54
+ return [{
55
+ type: 'error',
56
+ filePath: doc.filePath,
57
+ line: lineNumber,
58
+ code: 'INVALID_STATUS_VALUE',
59
+ message: `Invalid state "${value}" for ${markerType}. Allowed: ${allowed.join(', ')}`,
60
+ }];
61
+ }
62
+ return [];
63
+ }
64
+ export function checkDateFormat(doc, config) {
65
+ if (!config.validation.requireDates)
66
+ return [];
67
+ const statusSection = doc.sections.get('status');
68
+ if (!statusSection)
69
+ return [];
70
+ const violations = [];
71
+ let match;
72
+ const regex = new RegExp(DATE_LINE.source, 'gm');
73
+ while ((match = regex.exec(statusSection.rawBody)) !== null) {
74
+ const value = (match[1] ?? '').trim();
75
+ if (!ISO_DATE.test(value)) {
76
+ const matchLine = statusSection.rawBody
77
+ .substring(0, match.index)
78
+ .split('\n').length;
79
+ violations.push({
80
+ type: 'error',
81
+ filePath: doc.filePath,
82
+ line: statusSection.startLine + matchLine,
83
+ code: 'INVALID_DATE_FORMAT',
84
+ message: `Date "${value}" is not in ISO 8601 format (YYYY-MM-DD)`,
85
+ });
86
+ }
87
+ }
88
+ return violations;
89
+ }
90
+ export function checkBehaviourReference(doc, implFilePath) {
91
+ const behaviourSection = doc.sections.get('behaviour');
92
+ if (!behaviourSection)
93
+ return []; // Caught by checkRequiredSections
94
+ const refLine = behaviourSection.bodyLines.find(l => l.trim().length > 0);
95
+ if (!refLine) {
96
+ return [{
97
+ type: 'error',
98
+ filePath: doc.filePath,
99
+ line: behaviourSection.startLine + 1,
100
+ code: 'MISSING_BEHAVIOUR_REFERENCE',
101
+ message: 'Behaviour section is empty — expected a relative path to usecase.md',
102
+ }];
103
+ }
104
+ const ref = refLine.trim();
105
+ const resolvedPath = resolve(dirname(implFilePath), ref);
106
+ if (!existsSync(resolvedPath)) {
107
+ const lineNumber = behaviourSection.startLine +
108
+ behaviourSection.bodyLines.indexOf(refLine) + 1;
109
+ return [{
110
+ type: 'error',
111
+ filePath: doc.filePath,
112
+ line: lineNumber,
113
+ code: 'INVALID_BEHAVIOUR_REFERENCE',
114
+ message: `Behaviour reference "${ref}" does not resolve to an existing file`,
115
+ }];
116
+ }
117
+ return [];
118
+ }
119
+ export function checkDiagramSection(doc) {
120
+ const diagramSection = doc.sections.get('diagram');
121
+ if (!diagramSection)
122
+ return [];
123
+ const hasMermaidFence = /```mermaid/i.test(diagramSection.rawBody);
124
+ if (!hasMermaidFence) {
125
+ return [{
126
+ type: 'warning',
127
+ filePath: doc.filePath,
128
+ line: diagramSection.startLine,
129
+ code: 'DIAGRAM_MISSING_MERMAID_FENCE',
130
+ message: 'Diagram section exists but contains no mermaid code fence (```mermaid)',
131
+ }];
132
+ }
133
+ return [];
134
+ }
135
+ export function checkLinkSection(doc, node) {
136
+ if (node.marker === 'intent') {
137
+ const hasBehaviourChildren = node.children.some(c => c.marker === 'behaviour');
138
+ return checkManagedSection(doc, 'behaviours', 'Behaviours', hasBehaviourChildren);
139
+ }
140
+ if (node.marker === 'behaviour') {
141
+ const hasImplChildren = node.children.some(c => c.marker === 'impl');
142
+ return checkManagedSection(doc, 'implementations', 'Implementations', hasImplChildren);
143
+ }
144
+ return [];
145
+ }
146
+ function findSectionByPrefix(doc, prefix) {
147
+ for (const [key, value] of doc.sections) {
148
+ if (key === prefix || key.startsWith(prefix + ' '))
149
+ return value;
150
+ }
151
+ return undefined;
152
+ }
153
+ function checkManagedSection(doc, sectionKey, sectionTitle, hasChildren) {
154
+ const violations = [];
155
+ const section = findSectionByPrefix(doc, sectionKey);
156
+ if (hasChildren && !section) {
157
+ violations.push({
158
+ type: 'error',
159
+ filePath: doc.filePath,
160
+ code: 'MISSING_LINK_SECTION',
161
+ message: `Missing "## ${sectionTitle}" section — run \`taproot update\` to generate it`,
162
+ });
163
+ return violations;
164
+ }
165
+ if (!section)
166
+ return [];
167
+ const linkPattern = /\[.*?\]\((.+?)\)/g;
168
+ let m;
169
+ while ((m = linkPattern.exec(section.rawBody)) !== null) {
170
+ const linkTarget = m[1];
171
+ const resolved = resolve(dirname(doc.filePath), linkTarget);
172
+ if (!existsSync(resolved)) {
173
+ violations.push({
174
+ type: 'error',
175
+ filePath: doc.filePath,
176
+ code: 'STALE_LINK',
177
+ message: `STALE_LINK — link in ## ${sectionTitle} points to non-existent file: ${linkTarget}`,
178
+ });
179
+ }
180
+ }
181
+ return violations;
182
+ }
183
+ export function checkAcceptanceCriteria(doc, node, pack) {
184
+ const hasImplChildren = node.children.some(c => c.marker === 'impl');
185
+ const acKey = pack ? (pack['Acceptance Criteria']?.toLowerCase() ?? 'acceptance criteria') : 'acceptance criteria';
186
+ const criteriaSection = doc.sections.get(acKey);
187
+ if (hasImplChildren && !criteriaSection) {
188
+ return [{
189
+ type: 'warning',
190
+ filePath: doc.filePath,
191
+ code: 'MISSING_ACCEPTANCE_CRITERIA',
192
+ message: 'usecase.md has implementations but no "## Acceptance Criteria" section',
193
+ }];
194
+ }
195
+ if (!criteriaSection)
196
+ return [];
197
+ const violations = [];
198
+ const idPattern = /^\*\*(AC|NFR)-(\d+):/gm;
199
+ const seen = new Set();
200
+ let m;
201
+ while ((m = idPattern.exec(criteriaSection.rawBody)) !== null) {
202
+ const id = `${m[1]}-${m[2]}`;
203
+ if (seen.has(id)) {
204
+ const linesBefore = criteriaSection.rawBody.substring(0, m.index).split('\n').length;
205
+ violations.push({
206
+ type: 'error',
207
+ filePath: doc.filePath,
208
+ line: criteriaSection.startLine + linesBefore,
209
+ code: 'DUPLICATE_CRITERION_ID',
210
+ message: `Duplicate acceptance criterion ID "${id}" — each AC-N / NFR-N must be unique within the file`,
211
+ });
212
+ }
213
+ seen.add(id);
214
+ }
215
+ return violations;
216
+ }
217
+ export function validateFormat(doc, markerType, config, node, pack) {
218
+ const violations = [];
219
+ violations.push(...checkRequiredSections(doc, markerType, pack));
220
+ violations.push(...checkStatusValue(doc, markerType, config));
221
+ violations.push(...checkDateFormat(doc, config));
222
+ if (markerType === 'impl') {
223
+ violations.push(...checkBehaviourReference(doc, doc.filePath));
224
+ }
225
+ if (markerType === 'behaviour') {
226
+ violations.push(...checkDiagramSection(doc));
227
+ if (node)
228
+ violations.push(...checkAcceptanceCriteria(doc, node, pack));
229
+ }
230
+ if (node) {
231
+ violations.push(...checkLinkSection(doc, node));
232
+ }
233
+ return violations;
234
+ }
235
+ function titleCase(s) {
236
+ return s.replace(/\b\w/g, c => c.toUpperCase());
237
+ }
238
+ //# sourceMappingURL=format-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-rules.js","sourceRoot":"","sources":["../../src/validators/format-rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { FolderNode, Violation } from './types.js';
2
+ export declare function checkDuplicateMarkers(node: FolderNode): Violation[];
3
+ export declare function checkKebabCase(node: FolderNode): Violation[];
4
+ export declare function checkBehaviourParent(node: FolderNode): Violation[];
5
+ export declare function checkImplParent(node: FolderNode): Violation[];
6
+ export declare function checkOrphanFolder(node: FolderNode): Violation[];
7
+ export declare function checkEmptyFolder(node: FolderNode): Violation[];
8
+ export declare function validateStructure(root: FolderNode, options: {
9
+ strict: boolean;
10
+ }): Violation[];
@@ -0,0 +1,94 @@
1
+ import { join } from 'path';
2
+ import { flattenTree } from '../core/fs-walker.js';
3
+ const KEBAB_CASE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
4
+ export function checkDuplicateMarkers(node) {
5
+ if (node.markerFiles.length <= 1)
6
+ return [];
7
+ return [{
8
+ type: 'error',
9
+ filePath: node.absolutePath,
10
+ code: 'DUPLICATE_MARKERS',
11
+ message: `Folder contains multiple marker files: ${node.markerFiles.join(', ')}`,
12
+ }];
13
+ }
14
+ export function checkKebabCase(node) {
15
+ if (KEBAB_CASE.test(node.name))
16
+ return [];
17
+ return [{
18
+ type: 'error',
19
+ filePath: node.absolutePath,
20
+ code: 'INVALID_FOLDER_NAME',
21
+ message: `Folder name "${node.name}" does not match kebab-case pattern (^[a-z0-9]+(-[a-z0-9]+)*$)`,
22
+ }];
23
+ }
24
+ export function checkBehaviourParent(node) {
25
+ if (node.marker !== 'behaviour')
26
+ return [];
27
+ const parentMarker = node.parent?.marker ?? null;
28
+ if (parentMarker === 'intent' || parentMarker === 'behaviour')
29
+ return [];
30
+ return [{
31
+ type: 'error',
32
+ filePath: join(node.absolutePath, 'usecase.md'),
33
+ code: 'BEHAVIOUR_PARENT_INVALID',
34
+ message: `Behaviour folder "${node.name}" must be a child of an intent or behaviour folder, but parent is ${parentMarker === null ? 'the root or an unmarked folder' : `a "${parentMarker}" folder`}`,
35
+ }];
36
+ }
37
+ export function checkImplParent(node) {
38
+ if (node.marker !== 'impl')
39
+ return [];
40
+ if (node.parent?.marker === 'behaviour')
41
+ return [];
42
+ return [{
43
+ type: 'error',
44
+ filePath: join(node.absolutePath, 'impl.md'),
45
+ code: 'IMPL_PARENT_INVALID',
46
+ message: `Implementation folder "${node.name}" must be a child of a behaviour folder, but parent is ${node.parent?.marker === null ? 'an unmarked folder' : `a "${node.parent?.marker ?? 'root'}" folder`}`,
47
+ }];
48
+ }
49
+ export function checkOrphanFolder(node) {
50
+ if (node.marker !== null)
51
+ return [];
52
+ if (node.hasDescendantWithMarker)
53
+ return [];
54
+ // The taproot root itself (depth 0) is allowed to be empty initially
55
+ if (node.depth === 0)
56
+ return [];
57
+ return [{
58
+ type: 'error',
59
+ filePath: node.absolutePath,
60
+ code: 'ORPHAN_FOLDER',
61
+ message: `Folder "${node.name}" contains no marker file and has no descendant with a marker file`,
62
+ }];
63
+ }
64
+ export function checkEmptyFolder(node) {
65
+ if (node.marker === null)
66
+ return [];
67
+ if (node.children.length > 0)
68
+ return [];
69
+ // impl folders are leaf nodes by design — not empty warnings
70
+ if (node.marker === 'impl')
71
+ return [];
72
+ return [{
73
+ type: 'warning',
74
+ filePath: node.absolutePath,
75
+ code: 'EMPTY_FOLDER',
76
+ message: `${node.marker} folder "${node.name}" has no child folders`,
77
+ }];
78
+ }
79
+ export function validateStructure(root, options) {
80
+ const violations = [];
81
+ const nodes = flattenTree(root);
82
+ for (const node of nodes) {
83
+ violations.push(...checkDuplicateMarkers(node));
84
+ if (node !== root)
85
+ violations.push(...checkKebabCase(node));
86
+ violations.push(...checkBehaviourParent(node));
87
+ violations.push(...checkImplParent(node));
88
+ violations.push(...checkOrphanFolder(node));
89
+ if (options.strict)
90
+ violations.push(...checkEmptyFolder(node));
91
+ }
92
+ return violations;
93
+ }
94
+ //# sourceMappingURL=structure-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structure-rules.js","sourceRoot":"","sources":["../../src/validators/structure-rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ export type MarkerType = 'intent' | 'behaviour' | 'impl';
2
+ export interface FolderNode {
3
+ absolutePath: string;
4
+ relativePath: string;
5
+ name: string;
6
+ marker: MarkerType | null;
7
+ markerFiles: string[];
8
+ children: FolderNode[];
9
+ parent: FolderNode | null;
10
+ depth: number;
11
+ hasDescendantWithMarker: boolean;
12
+ }
13
+ export interface SectionContent {
14
+ heading: string;
15
+ startLine: number;
16
+ bodyLines: string[];
17
+ rawBody: string;
18
+ }
19
+ export interface ParsedMarkdown {
20
+ filePath: string;
21
+ sections: Map<string, SectionContent>;
22
+ rawLines: string[];
23
+ }
24
+ export interface Violation {
25
+ type: 'error' | 'warning';
26
+ filePath: string;
27
+ line?: number;
28
+ code: string;
29
+ message: string;
30
+ }
31
+ export type DodConditionEntry = string | {
32
+ run: string;
33
+ name?: string;
34
+ correction?: string;
35
+ } | {
36
+ 'document-current': string;
37
+ } | {
38
+ 'check-if-affected': string;
39
+ } | {
40
+ 'check-if-affected-by': string;
41
+ } | {
42
+ 'check': string;
43
+ };
44
+ export interface TaprootConfig {
45
+ version: number;
46
+ root: string;
47
+ commitPattern: string;
48
+ commitTrailer: string;
49
+ agents: string[];
50
+ language?: string;
51
+ vocabulary?: Record<string, string>;
52
+ validation: {
53
+ requireDates: boolean;
54
+ requireStatus: boolean;
55
+ allowedIntentStates: string[];
56
+ allowedBehaviourStates: string[];
57
+ allowedImplStates: string[];
58
+ };
59
+ hooks: {
60
+ preCommit: string[];
61
+ };
62
+ ci: {
63
+ onPr: string[];
64
+ onMerge: string[];
65
+ };
66
+ definitionOfDone?: DodConditionEntry[];
67
+ definitionOfReady?: DodConditionEntry[];
68
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validators/types.ts"],"names":[],"mappings":""}
package/docs/agents.md ADDED
@@ -0,0 +1,88 @@
1
+ # Agent Setup
2
+
3
+ Taproot works with any AI assistant that can read files. Run `taproot init --agent <name>` to generate the adapter for your assistant. The adapter installs the agent-specific integration files (slash commands, rules, or instruction files) that teach the agent how to use Taproot.
4
+
5
+ ## Supported Agents
6
+
7
+ | Agent | Support Tier | Command | What gets installed |
8
+ |-------|-------------|---------|---------------------|
9
+ | Claude Code | **Tier 1** — fully supported | `taproot init --agent claude` | `.claude/commands/tr-*.md` — one file per slash command |
10
+ | Gemini CLI | **Tier 2** — implemented & tested | `taproot init --agent gemini` | `.gemini/commands/tr-*.toml` — one file per command |
11
+ | Cursor | **Tier 3** — community supported | `taproot init --agent cursor` | `.cursor/rules/taproot.md` — project-wide rules |
12
+ | GitHub Copilot | **Tier 3** — community supported | `taproot init --agent copilot` | `.github/copilot-instructions.md` |
13
+ | Windsurf | **Tier 3** — community supported | `taproot init --agent windsurf` | `.windsurfrules` |
14
+ | Any agent | **Tier 3** — community supported | `taproot init --agent generic` | `AGENTS.md` — agent-agnostic instructions |
15
+ | All agents | — | `taproot init --agent all` | All of the above |
16
+
17
+ **Tier 1** agents are fully tested with all features supported and covered in CI. **Tier 2** agents have working adapters with basic end-to-end validation. **Tier 3** agents are community-supported — adapters are generated but not officially validated end-to-end. Feedback and fixes welcome.
18
+
19
+ You can install multiple adapters in the same project. If your team uses Cursor and you use Claude Code, run `taproot init --agent all`.
20
+
21
+ When Taproot is updated, run `taproot update` to refresh the adapter files to the current version. The update command also removes stale files from older layouts.
22
+
23
+ ---
24
+
25
+ ## Skills (Claude Code)
26
+
27
+ Claude Code skills are the richest integration. Each skill is a self-contained markdown file that defines a complete interactive workflow — inputs, steps, output format, and CLI dependencies. The agent reads the skill file when you invoke the command and follows it precisely.
28
+
29
+ After `taproot init --agent claude --with-skills`, skills are available as `/tr-*` slash commands:
30
+
31
+ | Command | Purpose |
32
+ |---------|---------|
33
+ | `/tr-guide` | Onboarding walkthrough — explains the hierarchy, lists commands, tailors the guide to whether your project already has intents |
34
+ | `/tr-ineed` | Route a natural language requirement to the right place; runs structured discovery for vague requirements before placement |
35
+ | `/tr-intent` | Create or refine a business intent document with stakeholders and success criteria |
36
+ | `/tr-behaviour` | Define a UseCase under an intent or another behaviour; walks alternate flows and error conditions proactively |
37
+ | `/tr-decompose` | Break an intent into the set of behaviours needed to fulfil it |
38
+ | `/tr-implement` | Implement a behaviour: write code, write tests, create `impl.md`, commit with conventional tag |
39
+ | `/tr-trace` | Navigate the hierarchy in any direction — file to intent (bottom-up), intent to code (top-down), or across siblings |
40
+ | `/tr-status` | Coverage dashboard: shows what's implemented, what's planned, what's stale, and what to do next |
41
+ | `/tr-review` | Stress-test a single artifact — challenges assumptions, identifies gaps, asks adversarial questions |
42
+ | `/tr-review-all` | Comprehensive review of an entire subtree — structural checks, cross-cutting analysis, per-artifact findings |
43
+ | `/tr-refine` | Update a behaviour spec based on post-implementation learnings |
44
+ | `/tr-promote` | Escalate a finding from implementation or behaviour level up to the intent |
45
+ | `/tr-analyse-change` | Impact analysis before editing an existing artifact — identifies downstream breakage and related behaviours |
46
+ | `/tr-plan` | Surface the next independently-implementable work item from the hierarchy |
47
+ | `/tr-discover` | Reverse-engineer an existing codebase into a taproot hierarchy through structured, interactive discovery |
48
+ | `/tr-grill-me` | Interview you relentlessly about a plan or design — one decision branch at a time, recommended answer first |
49
+ | `/tr-research` | Research a domain or technical subject before speccing — scans local resources, searches the web, optionally grills domain experts |
50
+ | `/tr-sweep` | Apply a uniform task to a filtered set of hierarchy files — enumerate, confirm, then call `taproot apply` per file |
51
+
52
+ ### How skills work
53
+
54
+ Skills are portable markdown files installed to `.taproot/skills/`. They are not IDE plugins or extensions — any agent that can read files can follow a skill. The Claude Code adapter generates `.claude/commands/tr-*.md` files that reference the skills; invoking `/tr-intent` tells Claude to load and follow `skills/intent.md`.
55
+
56
+ This means:
57
+ - Skills work even if the Claude Code extension is updated or changed
58
+ - You can read any skill file to understand exactly what the agent will do
59
+ - You can customise a skill by editing its file in `.taproot/skills/`
60
+ - Skills stay in sync with Taproot via `taproot update`
61
+
62
+ ### Updating skills
63
+
64
+ ```bash
65
+ taproot update
66
+ ```
67
+
68
+ This refreshes all adapter files and skill definitions to the current version. Run it after upgrading the `taproot` package.
69
+
70
+ ---
71
+
72
+ ## Non-Claude agents
73
+
74
+ For Cursor, Copilot, and Windsurf, the adapter installs a rules or instructions file. These give the agent context on the taproot hierarchy, the document formats, and the commit convention — enough to read and navigate the hierarchy, write conformant documents, and use the CLI commands correctly.
75
+
76
+ These adapters do not provide slash commands. Instead, refer to the relevant document or hierarchy path in your prompt:
77
+
78
+ ```
79
+ Look at taproot/password-reset/request-reset/usecase.md and implement the rate limiting behaviour.
80
+ ```
81
+
82
+ Or ask the agent to run CLI commands directly:
83
+
84
+ ```
85
+ Run taproot validate-format and fix any violations in the hierarchy.
86
+ ```
87
+
88
+ The `AGENTS.md` generic adapter works with any agent and can also be used as documentation for team members who are new to Taproot.
@@ -0,0 +1,53 @@
1
+ # Architecture
2
+
3
+ Architectural decisions, constraints, and patterns for the taproot codebase. Every implementation must be checked against these before coding begins (enforced via `definitionOfReady`).
4
+
5
+ ---
6
+
7
+ ## Principles
8
+
9
+ **Filesystem as data model** — No external database or service. All hierarchy state lives in git-versioned markdown files under `taproot/`. No runtime state is persisted outside the filesystem.
10
+
11
+ **Immutable config after load** — `loadConfig()` is called once at command entry; the resolved config object is passed down. Commands never re-read the config file mid-execution.
12
+
13
+ **External I/O only at command boundaries** — File reads/writes, git invocations, and process spawns happen in command handlers (`src/commands/`), not in core logic (`src/core/`). Core modules receive data, not file paths, wherever practical.
14
+
15
+ **Stateless CLI commands** — Each CLI invocation is independent. No daemon, no persistent process, no shared mutable state between invocations.
16
+
17
+ **Agent-agnostic output** — Skill files and adapter files must be plain markdown/text. No agent-specific syntax in the hierarchy documents themselves.
18
+
19
+ ---
20
+
21
+ ## Constraints
22
+
23
+ **No global mutable state** — No module-level variables that accumulate state across calls. Functions that need shared state receive it as parameters.
24
+
25
+ **`taproot/` contains only requirement documents** — Skills, config, and framework files live in `.taproot/`. Never mix framework files into the requirements hierarchy.
26
+
27
+ **`taproot/_sessions/` is the scratch space for agent session state** — Skills that persist resumable session data (phase progress, confirmed items, open questions) write to `taproot/_sessions/<skill-name>-status.md`. This folder is not a formal hierarchy document and is excluded from `taproot validate-structure`. Use `_sessions/`, not `_brainstorms/` — the latter implies ideation rather than structured state.
28
+
29
+ **Markdown is the schema** — All hierarchy documents (intent.md, usecase.md, impl.md) must be valid CommonMark. No proprietary extensions.
30
+
31
+ **Error messages must be actionable** — Every error surface (CLI output, DoD/DoR failures, validate-format output) must include a correction hint, not just a description of what failed.
32
+
33
+ **No raw exceptions to the user** — The CLI top-level handler catches all thrown errors and prints only the message, never a stack trace or Node.js exception dump. `process.exitCode = 1` is set; the process exits cleanly. Stack traces are for tests and debugging only.
34
+
35
+ ---
36
+
37
+ ## Module boundaries
38
+
39
+ | Layer | Path | Responsibility |
40
+ |---|---|---|
41
+ | Commands | `src/commands/` | CLI registration, I/O, orchestration |
42
+ | Core | `src/core/` | Pure logic: parsing, validation, config, runners |
43
+ | Validators | `src/validators/` | Types and structural validation rules |
44
+ | Adapters | `src/adapters/` | Agent-specific adapter/skill generation |
45
+ | Templates | `src/templates/` | Document scaffolding strings |
46
+
47
+ Core modules must not import from commands. Commands may import from core, validators, adapters, and templates.
48
+
49
+ ---
50
+
51
+ ## Testing
52
+
53
+ Integration tests use real temporary directories (`mkdtempSync`) — no mocking the filesystem. Config in tests must be written to `.taproot/settings.yaml` (not `.taproot/settings.yaml`).