@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.
- package/README.md +88 -0
- package/dist/adapters/index.d.ts +20 -0
- package/dist/adapters/index.js +452 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/acceptance-check.d.ts +26 -0
- package/dist/commands/acceptance-check.js +213 -0
- package/dist/commands/acceptance-check.js.map +1 -0
- package/dist/commands/check-orphans.d.ts +8 -0
- package/dist/commands/check-orphans.js +157 -0
- package/dist/commands/check-orphans.js.map +1 -0
- package/dist/commands/commithook.d.ts +15 -0
- package/dist/commands/commithook.js +389 -0
- package/dist/commands/commithook.js.map +1 -0
- package/dist/commands/coverage.d.ts +41 -0
- package/dist/commands/coverage.js +390 -0
- package/dist/commands/coverage.js.map +1 -0
- package/dist/commands/dod.d.ts +13 -0
- package/dist/commands/dod.js +141 -0
- package/dist/commands/dod.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +378 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link-commits.d.ts +12 -0
- package/dist/commands/link-commits.js +126 -0
- package/dist/commands/link-commits.js.map +1 -0
- package/dist/commands/overview.d.ts +6 -0
- package/dist/commands/overview.js +192 -0
- package/dist/commands/overview.js.map +1 -0
- package/dist/commands/plan.d.ts +23 -0
- package/dist/commands/plan.js +167 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/sync-check.d.ts +8 -0
- package/dist/commands/sync-check.js +118 -0
- package/dist/commands/sync-check.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +309 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate-format.d.ts +8 -0
- package/dist/commands/validate-format.js +93 -0
- package/dist/commands/validate-format.js.map +1 -0
- package/dist/commands/validate-structure.d.ts +8 -0
- package/dist/commands/validate-structure.js +29 -0
- package/dist/commands/validate-structure.js.map +1 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.js +86 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/configuration.d.ts +7 -0
- package/dist/core/configuration.js +112 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/dod-runner.d.ts +20 -0
- package/dist/core/dod-runner.js +233 -0
- package/dist/core/dod-runner.js.map +1 -0
- package/dist/core/dor-runner.d.ts +18 -0
- package/dist/core/dor-runner.js +156 -0
- package/dist/core/dor-runner.js.map +1 -0
- package/dist/core/fs-walker.d.ts +5 -0
- package/dist/core/fs-walker.js +74 -0
- package/dist/core/fs-walker.js.map +1 -0
- package/dist/core/git.d.ts +24 -0
- package/dist/core/git.js +76 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/impl-reader.d.ts +8 -0
- package/dist/core/impl-reader.js +39 -0
- package/dist/core/impl-reader.js.map +1 -0
- package/dist/core/language.d.ts +39 -0
- package/dist/core/language.js +159 -0
- package/dist/core/language.js.map +1 -0
- package/dist/core/markdown-parser.d.ts +3 -0
- package/dist/core/markdown-parser.js +37 -0
- package/dist/core/markdown-parser.js.map +1 -0
- package/dist/core/reporter.d.ts +3 -0
- package/dist/core/reporter.js +33 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +126 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/validators/format-rules.d.ts +10 -0
- package/dist/validators/format-rules.js +238 -0
- package/dist/validators/format-rules.js.map +1 -0
- package/dist/validators/structure-rules.d.ts +10 -0
- package/dist/validators/structure-rules.js +94 -0
- package/dist/validators/structure-rules.js.map +1 -0
- package/dist/validators/types.d.ts +68 -0
- package/dist/validators/types.js +2 -0
- package/dist/validators/types.js.map +1 -0
- package/docs/agents.md +88 -0
- package/docs/architecture.md +53 -0
- package/docs/cli.md +226 -0
- package/docs/concepts.md +268 -0
- package/docs/configuration.md +255 -0
- package/docs/demo.svg +111 -0
- package/docs/patterns.md +118 -0
- package/docs/security.md +95 -0
- package/docs/workflows.md +151 -0
- package/languages/de.json +20 -0
- package/languages/en.json +20 -0
- package/languages/es.json +20 -0
- package/languages/fr.json +20 -0
- package/languages/ja.json +20 -0
- package/languages/pt.json +20 -0
- package/package.json +54 -0
- package/skills/analyse-change.md +101 -0
- package/skills/behaviour.md +179 -0
- package/skills/bug.md +70 -0
- package/skills/commit.md +99 -0
- package/skills/decompose.md +101 -0
- package/skills/discover.md +392 -0
- package/skills/grill-me.md +65 -0
- package/skills/guide.md +118 -0
- package/skills/implement.md +149 -0
- package/skills/ineed.md +147 -0
- package/skills/intent.md +104 -0
- package/skills/plan.md +63 -0
- package/skills/promote.md +69 -0
- package/skills/refine.md +78 -0
- package/skills/research.md +122 -0
- package/skills/review-all.md +92 -0
- package/skills/review.md +80 -0
- package/skills/status.md +103 -0
- package/skills/sweep.md +89 -0
- package/skills/trace.md +151 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface GitCommit {
|
|
2
|
+
hash: string;
|
|
3
|
+
subject: string;
|
|
4
|
+
body: string;
|
|
5
|
+
date: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function isGitRepo(cwd: string): boolean;
|
|
8
|
+
export declare function getRepoRoot(cwd: string): string | null;
|
|
9
|
+
export declare function gitLog(options: {
|
|
10
|
+
since?: string;
|
|
11
|
+
cwd: string;
|
|
12
|
+
}): GitCommit[];
|
|
13
|
+
export declare function commitExists(hash: string, cwd: string): boolean;
|
|
14
|
+
/** Returns the date of the last commit that touched this file, or null. */
|
|
15
|
+
export declare function fileLastCommitDate(filePath: string, cwd: string): Date | null;
|
|
16
|
+
/** Returns the filesystem mtime of a file, or null if it doesn't exist. */
|
|
17
|
+
export declare function fileMtime(filePath: string): Date | null;
|
|
18
|
+
/**
|
|
19
|
+
* Parse a commit looking for a taproot path reference.
|
|
20
|
+
* Supports:
|
|
21
|
+
* Subject: taproot(some/path): message
|
|
22
|
+
* Body trailer: Taproot: some/path
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractTaprootPath(commit: GitCommit, commitPattern: string, trailerKey: string): string | null;
|
package/dist/core/git.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { statSync } from 'fs';
|
|
3
|
+
function git(args, cwd) {
|
|
4
|
+
const result = spawnSync('git', args, { cwd, encoding: 'utf-8' });
|
|
5
|
+
return {
|
|
6
|
+
stdout: result.stdout?.trim() ?? '',
|
|
7
|
+
ok: result.status === 0,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function isGitRepo(cwd) {
|
|
11
|
+
return git(['rev-parse', '--git-dir'], cwd).ok;
|
|
12
|
+
}
|
|
13
|
+
export function getRepoRoot(cwd) {
|
|
14
|
+
const r = git(['rev-parse', '--show-toplevel'], cwd);
|
|
15
|
+
return r.ok ? r.stdout : null;
|
|
16
|
+
}
|
|
17
|
+
export function gitLog(options) {
|
|
18
|
+
const args = ['log', '--format=%H%x00%s%x00%b%x00%aI%x00---COMMIT---'];
|
|
19
|
+
if (options.since)
|
|
20
|
+
args.push(`--since=${options.since}`);
|
|
21
|
+
const { stdout, ok } = git(args, options.cwd);
|
|
22
|
+
if (!ok || !stdout)
|
|
23
|
+
return [];
|
|
24
|
+
return stdout
|
|
25
|
+
.split('---COMMIT---')
|
|
26
|
+
.map(s => s.trim())
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map(block => {
|
|
29
|
+
const parts = block.split('\x00');
|
|
30
|
+
return {
|
|
31
|
+
hash: (parts[0] ?? '').trim(),
|
|
32
|
+
subject: (parts[1] ?? '').trim(),
|
|
33
|
+
body: (parts[2] ?? '').trim(),
|
|
34
|
+
date: (parts[3] ?? '').trim(),
|
|
35
|
+
};
|
|
36
|
+
})
|
|
37
|
+
.filter(c => c.hash.length === 40);
|
|
38
|
+
}
|
|
39
|
+
export function commitExists(hash, cwd) {
|
|
40
|
+
return git(['cat-file', '-e', `${hash}^{commit}`], cwd).ok;
|
|
41
|
+
}
|
|
42
|
+
/** Returns the date of the last commit that touched this file, or null. */
|
|
43
|
+
export function fileLastCommitDate(filePath, cwd) {
|
|
44
|
+
const r = git(['log', '-1', '--format=%aI', '--', filePath], cwd);
|
|
45
|
+
if (!r.ok || !r.stdout)
|
|
46
|
+
return null;
|
|
47
|
+
const d = new Date(r.stdout);
|
|
48
|
+
return isNaN(d.getTime()) ? null : d;
|
|
49
|
+
}
|
|
50
|
+
/** Returns the filesystem mtime of a file, or null if it doesn't exist. */
|
|
51
|
+
export function fileMtime(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
return statSync(filePath).mtime;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse a commit looking for a taproot path reference.
|
|
61
|
+
* Supports:
|
|
62
|
+
* Subject: taproot(some/path): message
|
|
63
|
+
* Body trailer: Taproot: some/path
|
|
64
|
+
*/
|
|
65
|
+
export function extractTaprootPath(commit, commitPattern, trailerKey) {
|
|
66
|
+
const subjectRegex = new RegExp(commitPattern);
|
|
67
|
+
const subjectMatch = subjectRegex.exec(commit.subject);
|
|
68
|
+
if (subjectMatch?.[1])
|
|
69
|
+
return subjectMatch[1].trim();
|
|
70
|
+
const trailerRegex = new RegExp(`^${trailerKey}:\\s*(.+)$`, 'm');
|
|
71
|
+
const trailerMatch = trailerRegex.exec(commit.body);
|
|
72
|
+
if (trailerMatch?.[1])
|
|
73
|
+
return trailerMatch[1].trim();
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/core/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ParsedMarkdown } from '../validators/types.js';
|
|
2
|
+
export interface ImplData {
|
|
3
|
+
behaviourRef: string | null;
|
|
4
|
+
sourceFiles: string[];
|
|
5
|
+
commits: string[];
|
|
6
|
+
testFiles: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function parseImplData(doc: ParsedMarkdown): ImplData;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Returns true if a backtick-quoted token looks like a file path (contains '/' or ends with a dotted extension). */
|
|
2
|
+
function isFilePath(token) {
|
|
3
|
+
return token.includes('/') || /\.\w+$/.test(token);
|
|
4
|
+
}
|
|
5
|
+
/** Extract a backtick-quoted value from a list item line: `- \`value\` — ...` */
|
|
6
|
+
function extractBacktickValues(lines) {
|
|
7
|
+
const result = [];
|
|
8
|
+
for (const line of lines) {
|
|
9
|
+
const match = /`([^`]+)`/.exec(line);
|
|
10
|
+
if (match?.[1])
|
|
11
|
+
result.push(match[1]);
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/** Extract file paths from a list of lines — skips backtick-quoted tokens that are not recognisable as file paths (e.g. function signatures, identifiers). */
|
|
16
|
+
function extractFilePaths(lines) {
|
|
17
|
+
return extractBacktickValues(lines).filter(isFilePath);
|
|
18
|
+
}
|
|
19
|
+
/** Extract commit hashes — 6-64 hex chars from backtick-quoted items */
|
|
20
|
+
function extractCommitHashes(lines) {
|
|
21
|
+
return extractBacktickValues(lines).filter(v => /^[0-9a-f]{6,64}$/i.test(v));
|
|
22
|
+
}
|
|
23
|
+
export function parseImplData(doc) {
|
|
24
|
+
const behaviourSection = doc.sections.get('behaviour');
|
|
25
|
+
const sourceSection = doc.sections.get('source files');
|
|
26
|
+
const commitsSection = doc.sections.get('commits');
|
|
27
|
+
const testsSection = doc.sections.get('tests');
|
|
28
|
+
// Behaviour ref: first non-empty line of the Behaviour section
|
|
29
|
+
const behaviourRef = behaviourSection?.bodyLines
|
|
30
|
+
.map(l => l.trim())
|
|
31
|
+
.find(l => l.length > 0) ?? null;
|
|
32
|
+
return {
|
|
33
|
+
behaviourRef,
|
|
34
|
+
sourceFiles: extractFilePaths(sourceSection?.bodyLines ?? []),
|
|
35
|
+
commits: extractCommitHashes(commitsSection?.bodyLines ?? []),
|
|
36
|
+
testFiles: extractFilePaths(testsSection?.bodyLines ?? []),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=impl-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impl-reader.js","sourceRoot":"","sources":["../../src/core/impl-reader.ts"],"names":[],"mappings":"AASA,qHAAqH;AACrH,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,iFAAiF;AACjF,SAAS,qBAAqB,CAAC,KAAe;IAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8JAA8J;AAC9J,SAAS,gBAAgB,CAAC,KAAe;IACvC,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,wEAAwE;AACxE,SAAS,mBAAmB,CAAC,KAAe;IAC1C,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAmB;IAC/C,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAE/C,+DAA+D;IAC/D,MAAM,YAAY,GAAG,gBAAgB,EAAE,SAAS;SAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IAEnC,OAAO;QACL,YAAY;QACZ,WAAW,EAAE,gBAAgB,CAAC,aAAa,EAAE,SAAS,IAAI,EAAE,CAAC;QAC7D,OAAO,EAAE,mBAAmB,CAAC,cAAc,EAAE,SAAS,IAAI,EAAE,CAAC;QAC7D,SAAS,EAAE,gBAAgB,CAAC,YAAY,EAAE,SAAS,IAAI,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { MarkerType } from '../validators/types.js';
|
|
2
|
+
export type LanguagePack = Record<string, string>;
|
|
3
|
+
/**
|
|
4
|
+
* Load a language pack by code. Returns null if unsupported or file not found.
|
|
5
|
+
* Falls back gracefully on missing keys — returns null only for wholly unknown codes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadLanguagePack(code: string): LanguagePack | null;
|
|
8
|
+
/** Returns the list of supported language codes. */
|
|
9
|
+
export declare function supportedLanguages(): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Return the structural keyword keys for conflict detection.
|
|
12
|
+
* Uses the provided pack, or falls back to the English pack.
|
|
13
|
+
* Always returns a non-empty list so structural keywords are always protected.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getStructuralKeys(pack: LanguagePack | null): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Substitute all language pack tokens in content.
|
|
18
|
+
* Single-pass, sorted by key length descending (longer keys matched first).
|
|
19
|
+
* Case-sensitive exact string replacement.
|
|
20
|
+
*/
|
|
21
|
+
export declare function substituteTokens(content: string, pack: LanguagePack): string;
|
|
22
|
+
/**
|
|
23
|
+
* Apply domain vocabulary overrides to content.
|
|
24
|
+
* Runs after the language pack substitution pass.
|
|
25
|
+
*
|
|
26
|
+
* - Declaration-order: keys processed in the order they appear in the map
|
|
27
|
+
* - Single-pass via placeholder trick (same mechanism as substituteTokens)
|
|
28
|
+
* - Structural keys (those present in the language pack) are excluded with a warning
|
|
29
|
+
* - Returns the substituted content and any conflict warnings
|
|
30
|
+
*/
|
|
31
|
+
export declare function applyVocabulary(content: string, vocab: Record<string, string>, structuralKeys: string[]): {
|
|
32
|
+
result: string;
|
|
33
|
+
warnings: string[];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Return required section keys for a given marker type, localised via the pack.
|
|
37
|
+
* impl.md sections are always English (excluded from localisation by design).
|
|
38
|
+
*/
|
|
39
|
+
export declare function getLocalizedRequiredSections(markerType: MarkerType, pack: LanguagePack | null): string[];
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
// Packs live at src/languages/ → compiled to dist/languages/ (two levels up from dist/core/)
|
|
6
|
+
const LANGUAGES_DIR = resolve(__dirname, '..', '..', 'languages');
|
|
7
|
+
const SUPPORTED_LANGUAGES = ['en', 'de', 'fr', 'es', 'ja', 'pt'];
|
|
8
|
+
/** English required section keys per marker type (lowercase, as stored in ParsedMarkdown.sections). */
|
|
9
|
+
const ENGLISH_REQUIRED_SECTIONS = {
|
|
10
|
+
intent: ['stakeholders', 'goal', 'success criteria', 'status'],
|
|
11
|
+
behaviour: ['actor', 'preconditions', 'main flow', 'postconditions', 'status'],
|
|
12
|
+
impl: ['behaviour', 'commits', 'tests', 'status'],
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Load a language pack by code. Returns null if unsupported or file not found.
|
|
16
|
+
* Falls back gracefully on missing keys — returns null only for wholly unknown codes.
|
|
17
|
+
*/
|
|
18
|
+
export function loadLanguagePack(code) {
|
|
19
|
+
if (!SUPPORTED_LANGUAGES.includes(code))
|
|
20
|
+
return null;
|
|
21
|
+
if (code === 'en')
|
|
22
|
+
return buildEnglishPack();
|
|
23
|
+
const packPath = resolve(LANGUAGES_DIR, `${code}.json`);
|
|
24
|
+
if (!existsSync(packPath))
|
|
25
|
+
return null;
|
|
26
|
+
try {
|
|
27
|
+
const raw = JSON.parse(readFileSync(packPath, 'utf-8'));
|
|
28
|
+
// Fill missing keys from English defaults with a warning
|
|
29
|
+
const english = buildEnglishPack();
|
|
30
|
+
const result = { ...english };
|
|
31
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
32
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
33
|
+
result[key] = value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Returns the list of supported language codes. */
|
|
43
|
+
export function supportedLanguages() {
|
|
44
|
+
return [...SUPPORTED_LANGUAGES];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Return the structural keyword keys for conflict detection.
|
|
48
|
+
* Uses the provided pack, or falls back to the English pack.
|
|
49
|
+
* Always returns a non-empty list so structural keywords are always protected.
|
|
50
|
+
*/
|
|
51
|
+
export function getStructuralKeys(pack) {
|
|
52
|
+
if (pack)
|
|
53
|
+
return Object.keys(pack);
|
|
54
|
+
const english = buildEnglishPack();
|
|
55
|
+
return Object.keys(english);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Substitute all language pack tokens in content.
|
|
59
|
+
* Single-pass, sorted by key length descending (longer keys matched first).
|
|
60
|
+
* Case-sensitive exact string replacement.
|
|
61
|
+
*/
|
|
62
|
+
export function substituteTokens(content, pack) {
|
|
63
|
+
if (!pack || Object.keys(pack).length === 0)
|
|
64
|
+
return content;
|
|
65
|
+
// Sort entries by key length descending so "Main Flow" is matched before "Flow"
|
|
66
|
+
const entries = Object.entries(pack).sort((a, b) => b[0].length - a[0].length);
|
|
67
|
+
// Single-pass: replace each key with a placeholder, then swap placeholders for values
|
|
68
|
+
// This prevents double-substitution when a value contains another key
|
|
69
|
+
const placeholders = [];
|
|
70
|
+
let result = content;
|
|
71
|
+
for (let i = 0; i < entries.length; i++) {
|
|
72
|
+
const [key, value] = entries[i];
|
|
73
|
+
if (key === value)
|
|
74
|
+
continue; // English pack — no-op
|
|
75
|
+
const placeholder = `\x00TAPROOT_LANG_${i}\x00`;
|
|
76
|
+
placeholders.push([placeholder, value]);
|
|
77
|
+
result = result.split(key).join(placeholder);
|
|
78
|
+
}
|
|
79
|
+
for (const [placeholder, value] of placeholders) {
|
|
80
|
+
result = result.split(placeholder).join(value);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Apply domain vocabulary overrides to content.
|
|
86
|
+
* Runs after the language pack substitution pass.
|
|
87
|
+
*
|
|
88
|
+
* - Declaration-order: keys processed in the order they appear in the map
|
|
89
|
+
* - Single-pass via placeholder trick (same mechanism as substituteTokens)
|
|
90
|
+
* - Structural keys (those present in the language pack) are excluded with a warning
|
|
91
|
+
* - Returns the substituted content and any conflict warnings
|
|
92
|
+
*/
|
|
93
|
+
export function applyVocabulary(content, vocab, structuralKeys) {
|
|
94
|
+
const warnings = [];
|
|
95
|
+
const structuralLower = new Set(structuralKeys.map(k => k.toLowerCase()));
|
|
96
|
+
// Filter entries: skip structural conflicts
|
|
97
|
+
const entries = [];
|
|
98
|
+
for (const [key, value] of Object.entries(vocab)) {
|
|
99
|
+
if (structuralLower.has(key.toLowerCase())) {
|
|
100
|
+
warnings.push(`Vocabulary override '${key}' conflicts with a structural keyword — structural keywords take precedence; override ignored`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
entries.push([key, value]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (entries.length === 0)
|
|
107
|
+
return { result: content, warnings };
|
|
108
|
+
// Single-pass substitution in declaration order using placeholders
|
|
109
|
+
const placeholders = [];
|
|
110
|
+
let result = content;
|
|
111
|
+
for (let i = 0; i < entries.length; i++) {
|
|
112
|
+
const [key, value] = entries[i];
|
|
113
|
+
const placeholder = `\x00TAPROOT_VOCAB_${i}\x00`;
|
|
114
|
+
placeholders.push([placeholder, value]);
|
|
115
|
+
result = result.split(key).join(placeholder);
|
|
116
|
+
}
|
|
117
|
+
for (const [placeholder, value] of placeholders) {
|
|
118
|
+
result = result.split(placeholder).join(value);
|
|
119
|
+
}
|
|
120
|
+
return { result, warnings };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Return required section keys for a given marker type, localised via the pack.
|
|
124
|
+
* impl.md sections are always English (excluded from localisation by design).
|
|
125
|
+
*/
|
|
126
|
+
export function getLocalizedRequiredSections(markerType, pack) {
|
|
127
|
+
const english = ENGLISH_REQUIRED_SECTIONS[markerType];
|
|
128
|
+
if (!pack || markerType === 'impl')
|
|
129
|
+
return english;
|
|
130
|
+
return english.map(key => {
|
|
131
|
+
// Find the matching pack entry: pack keys are Title Case, section keys are lowercase
|
|
132
|
+
const packKey = Object.keys(pack).find(k => k.toLowerCase() === key);
|
|
133
|
+
if (!packKey)
|
|
134
|
+
return key; // fallback to English
|
|
135
|
+
return pack[packKey].toLowerCase();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
function buildEnglishPack() {
|
|
139
|
+
const packPath = resolve(LANGUAGES_DIR, 'en.json');
|
|
140
|
+
if (existsSync(packPath)) {
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(readFileSync(packPath, 'utf-8'));
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// fall through to hardcoded defaults
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Hardcoded fallback if en.json not found (should never happen in a valid install)
|
|
149
|
+
return {
|
|
150
|
+
Goal: 'Goal', Actor: 'Actor', Preconditions: 'Preconditions',
|
|
151
|
+
'Main Flow': 'Main Flow', 'Alternate Flows': 'Alternate Flows',
|
|
152
|
+
Postconditions: 'Postconditions', 'Error Conditions': 'Error Conditions',
|
|
153
|
+
'Acceptance Criteria': 'Acceptance Criteria', Status: 'Status',
|
|
154
|
+
Stakeholders: 'Stakeholders', 'Success Criteria': 'Success Criteria',
|
|
155
|
+
Given: 'Given', When: 'When', Then: 'Then',
|
|
156
|
+
specified: 'specified', implemented: 'implemented', complete: 'complete', active: 'active',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=language.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/core/language.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function parseMarkdown(filePath, content) {
|
|
2
|
+
const rawLines = content.split('\n');
|
|
3
|
+
const sections = new Map();
|
|
4
|
+
let currentHeading = null;
|
|
5
|
+
let currentStart = 0;
|
|
6
|
+
let currentBody = [];
|
|
7
|
+
const flushSection = (endIndex) => {
|
|
8
|
+
if (currentHeading === null)
|
|
9
|
+
return;
|
|
10
|
+
const rawBody = currentBody.join('\n').trim();
|
|
11
|
+
sections.set(currentHeading.toLowerCase(), {
|
|
12
|
+
heading: currentHeading,
|
|
13
|
+
startLine: currentStart,
|
|
14
|
+
bodyLines: currentBody,
|
|
15
|
+
rawBody,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
19
|
+
const line = rawLines[i] ?? '';
|
|
20
|
+
const match = line.match(/^##\s+(.+)$/);
|
|
21
|
+
if (match) {
|
|
22
|
+
flushSection(i);
|
|
23
|
+
currentHeading = (match[1] ?? '').trim();
|
|
24
|
+
currentStart = i + 1; // 1-indexed line number of the heading itself
|
|
25
|
+
currentBody = [];
|
|
26
|
+
}
|
|
27
|
+
else if (currentHeading !== null) {
|
|
28
|
+
currentBody.push(line);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
flushSection(rawLines.length);
|
|
32
|
+
return { filePath, sections, rawLines };
|
|
33
|
+
}
|
|
34
|
+
export function getSectionLine(doc, sectionKey) {
|
|
35
|
+
return doc.sections.get(sectionKey.toLowerCase())?.startLine;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=markdown-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-parser.js","sourceRoot":"","sources":["../../src/core/markdown-parser.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,OAAe;IAC7D,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEnD,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,WAAW,GAAa,EAAE,CAAC;IAE/B,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,EAAE;QACxC,IAAI,cAAc,KAAK,IAAI;YAAE,OAAO;QACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,WAAW,EAAE,EAAE;YACzC,OAAO,EAAE,cAAc;YACvB,SAAS,EAAE,YAAY;YACvB,SAAS,EAAE,WAAW;YACtB,OAAO;SACR,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,cAAc,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACzC,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,8CAA8C;YACpE,WAAW,GAAG,EAAE,CAAC;QACnB,CAAC;aAAM,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YACnC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9B,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAmB,EAAE,UAAkB;IACpE,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,EAAE,SAAS,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export function renderViolations(violations) {
|
|
3
|
+
if (violations.length === 0) {
|
|
4
|
+
return chalk.green('✓ No violations found.\n');
|
|
5
|
+
}
|
|
6
|
+
const byFile = new Map();
|
|
7
|
+
for (const v of violations) {
|
|
8
|
+
const list = byFile.get(v.filePath) ?? [];
|
|
9
|
+
list.push(v);
|
|
10
|
+
byFile.set(v.filePath, list);
|
|
11
|
+
}
|
|
12
|
+
const lines = [];
|
|
13
|
+
for (const [filePath, fileViolations] of byFile) {
|
|
14
|
+
lines.push('');
|
|
15
|
+
lines.push(chalk.underline(filePath));
|
|
16
|
+
for (const v of fileViolations) {
|
|
17
|
+
const location = v.line !== undefined ? `:${v.line}` : '';
|
|
18
|
+
const severity = v.type === 'error' ? chalk.red('error') : chalk.yellow('warning');
|
|
19
|
+
lines.push(` ${filePath}${location} ${severity} ${v.message} ${chalk.gray(v.code)}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const errors = violations.filter(v => v.type === 'error').length;
|
|
23
|
+
const warnings = violations.filter(v => v.type === 'warning').length;
|
|
24
|
+
lines.push('');
|
|
25
|
+
const summary = `${errors} error${errors !== 1 ? 's' : ''}, ${warnings} warning${warnings !== 1 ? 's' : ''}`;
|
|
26
|
+
lines.push(errors > 0 ? chalk.red(summary) : chalk.yellow(summary));
|
|
27
|
+
lines.push('');
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
export function exitCode(violations) {
|
|
31
|
+
return violations.some(v => v.type === 'error') ? 1 : 0;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,UAAU,gBAAgB,CAAC,UAAuB;IACtD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACb,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,MAAM,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACnF,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,GAAG,QAAQ,KAAK,QAAQ,KAAK,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACjE,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACrE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,MAAM,OAAO,GAAG,GAAG,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7G,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAAuB;IAC9C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function intentTemplate(date: string): string;
|
|
2
|
+
export declare function behaviourTemplate(date: string): string;
|
|
3
|
+
export declare function implTemplate(date: string, behaviourRef?: string): string;
|
|
4
|
+
export declare const SECTION_PLACEHOLDERS: Record<string, Record<string, string>>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export function intentTemplate(date) {
|
|
2
|
+
return `# Intent: <Title>
|
|
3
|
+
|
|
4
|
+
## Stakeholders
|
|
5
|
+
- <Role>: <Name or team> — <their interest in this intent>
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
<1-3 sentences describing the desired outcome from the stakeholder's perspective>
|
|
9
|
+
|
|
10
|
+
## Success Criteria
|
|
11
|
+
- [ ] <Measurable criterion 1>
|
|
12
|
+
- [ ] <Measurable criterion 2>
|
|
13
|
+
|
|
14
|
+
## Constraints
|
|
15
|
+
- <Constraint 1 (regulatory, technical, timeline, etc.)>
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
- **State:** draft
|
|
19
|
+
- **Created:** ${date}
|
|
20
|
+
- **Last reviewed:** ${date}
|
|
21
|
+
|
|
22
|
+
## Notes
|
|
23
|
+
<Free-form context, links to external docs, meeting notes, etc.>
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
export function behaviourTemplate(date) {
|
|
27
|
+
return `# Behaviour: <Title>
|
|
28
|
+
|
|
29
|
+
## Actor
|
|
30
|
+
<Who or what initiates this behaviour>
|
|
31
|
+
|
|
32
|
+
## Preconditions
|
|
33
|
+
- <What must be true before this behaviour can occur>
|
|
34
|
+
|
|
35
|
+
## Main Flow
|
|
36
|
+
1. <Step 1>
|
|
37
|
+
2. <Step 2>
|
|
38
|
+
3. <Step 3>
|
|
39
|
+
|
|
40
|
+
## Alternate Flows
|
|
41
|
+
### <Alternate flow name>
|
|
42
|
+
- **Trigger:** <When does this alternate flow occur?>
|
|
43
|
+
- **Steps:**
|
|
44
|
+
1. <Step>
|
|
45
|
+
2. <Step>
|
|
46
|
+
|
|
47
|
+
## Postconditions
|
|
48
|
+
- <What is true after successful completion>
|
|
49
|
+
|
|
50
|
+
## Error Conditions
|
|
51
|
+
- <Error scenario>: <Expected system response>
|
|
52
|
+
|
|
53
|
+
## Diagram
|
|
54
|
+
<!-- Optional: sequence, flowchart, or state diagram in Mermaid syntax -->
|
|
55
|
+
\`\`\`mermaid
|
|
56
|
+
sequenceDiagram
|
|
57
|
+
Actor->>System: <trigger>
|
|
58
|
+
System-->>Actor: <response>
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
- **State:** proposed
|
|
63
|
+
- **Created:** ${date}
|
|
64
|
+
- **Last reviewed:** ${date}
|
|
65
|
+
|
|
66
|
+
## Notes
|
|
67
|
+
<Edge cases, open questions, links to related behaviours>
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
export function implTemplate(date, behaviourRef = '../usecase.md') {
|
|
71
|
+
return `# Implementation: <Title>
|
|
72
|
+
|
|
73
|
+
## Behaviour
|
|
74
|
+
${behaviourRef}
|
|
75
|
+
|
|
76
|
+
## Design Decisions
|
|
77
|
+
- <Decision 1: what was chosen and why>
|
|
78
|
+
|
|
79
|
+
## Source Files
|
|
80
|
+
- \`<path/to/file.ts>\` — <brief description of what this file does for this implementation>
|
|
81
|
+
|
|
82
|
+
## Commits
|
|
83
|
+
- \`<hash>\` — <one-line summary>
|
|
84
|
+
|
|
85
|
+
## Tests
|
|
86
|
+
- \`<path/to/test-file.test.ts>\` — <what scenarios this test covers>
|
|
87
|
+
|
|
88
|
+
## Status
|
|
89
|
+
- **State:** planned
|
|
90
|
+
- **Created:** ${date}
|
|
91
|
+
- **Last verified:** ${date}
|
|
92
|
+
|
|
93
|
+
## Notes
|
|
94
|
+
<Technical debt, known limitations, future improvements>
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
export const SECTION_PLACEHOLDERS = {
|
|
98
|
+
intent: {
|
|
99
|
+
'stakeholders': '- <Role>: <Name or team> — <their interest in this intent>',
|
|
100
|
+
'goal': '<1-3 sentences describing the desired outcome from the stakeholder\'s perspective>',
|
|
101
|
+
'success criteria': '- [ ] <Measurable criterion 1>',
|
|
102
|
+
'constraints': '- <Constraint 1>',
|
|
103
|
+
'status': '- **State:** draft\n- **Created:** <YYYY-MM-DD>\n- **Last reviewed:** <YYYY-MM-DD>',
|
|
104
|
+
'notes': '<Free-form context>',
|
|
105
|
+
},
|
|
106
|
+
behaviour: {
|
|
107
|
+
'actor': '<Who or what initiates this behaviour>',
|
|
108
|
+
'preconditions': '- <What must be true before this behaviour can occur>',
|
|
109
|
+
'main flow': '1. <Step 1>\n2. <Step 2>',
|
|
110
|
+
'alternate flows': '### <Alternate flow name>\n- **Trigger:** <When?>\n- **Steps:**\n 1. <Step>',
|
|
111
|
+
'postconditions': '- <What is true after successful completion>',
|
|
112
|
+
'error conditions': '- <Error scenario>: <Expected system response>',
|
|
113
|
+
'status': '- **State:** proposed\n- **Created:** <YYYY-MM-DD>\n- **Last reviewed:** <YYYY-MM-DD>',
|
|
114
|
+
'notes': '<Edge cases, open questions>',
|
|
115
|
+
},
|
|
116
|
+
impl: {
|
|
117
|
+
'behaviour': '../usecase.md',
|
|
118
|
+
'design decisions': '- <Decision 1: what was chosen and why>',
|
|
119
|
+
'source files': '- `<path/to/file.ts>` — <description>',
|
|
120
|
+
'commits': '- `<hash>` — <one-line summary>',
|
|
121
|
+
'tests': '- `<path/to/test.ts>` — <what this covers>',
|
|
122
|
+
'status': '- **State:** planned\n- **Created:** <YYYY-MM-DD>\n- **Last verified:** <YYYY-MM-DD>',
|
|
123
|
+
'notes': '<Technical debt, known limitations>',
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO;;;;;;;;;;;;;;;;;iBAiBQ,IAAI;uBACE,IAAI;;;;CAI1B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCQ,IAAI;uBACE,IAAI;;;;CAI1B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,eAAuB,eAAe;IAC/E,OAAO;;;EAGP,YAAY;;;;;;;;;;;;;;;;iBAgBG,IAAI;uBACE,IAAI;;;;CAI1B,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAA2C;IAC1E,MAAM,EAAE;QACN,cAAc,EAAE,4DAA4D;QAC5E,MAAM,EAAE,oFAAoF;QAC5F,kBAAkB,EAAE,gCAAgC;QACpD,aAAa,EAAE,kBAAkB;QACjC,QAAQ,EAAE,oFAAoF;QAC9F,OAAO,EAAE,qBAAqB;KAC/B;IACD,SAAS,EAAE;QACT,OAAO,EAAE,wCAAwC;QACjD,eAAe,EAAE,uDAAuD;QACxE,WAAW,EAAE,0BAA0B;QACvC,iBAAiB,EAAE,8EAA8E;QACjG,gBAAgB,EAAE,8CAA8C;QAChE,kBAAkB,EAAE,gDAAgD;QACpE,QAAQ,EAAE,uFAAuF;QACjG,OAAO,EAAE,8BAA8B;KACxC;IACD,IAAI,EAAE;QACJ,WAAW,EAAE,eAAe;QAC5B,kBAAkB,EAAE,yCAAyC;QAC7D,cAAc,EAAE,uCAAuC;QACvD,SAAS,EAAE,iCAAiC;QAC5C,OAAO,EAAE,4CAA4C;QACrD,QAAQ,EAAE,sFAAsF;QAChG,OAAO,EAAE,qCAAqC;KAC/C;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ParsedMarkdown, MarkerType, TaprootConfig, Violation, FolderNode } from './types.js';
|
|
2
|
+
import type { LanguagePack } from '../core/language.js';
|
|
3
|
+
export declare function checkRequiredSections(doc: ParsedMarkdown, markerType: MarkerType, pack?: LanguagePack | null): Violation[];
|
|
4
|
+
export declare function checkStatusValue(doc: ParsedMarkdown, markerType: MarkerType, config: TaprootConfig): Violation[];
|
|
5
|
+
export declare function checkDateFormat(doc: ParsedMarkdown, config: TaprootConfig): Violation[];
|
|
6
|
+
export declare function checkBehaviourReference(doc: ParsedMarkdown, implFilePath: string): Violation[];
|
|
7
|
+
export declare function checkDiagramSection(doc: ParsedMarkdown): Violation[];
|
|
8
|
+
export declare function checkLinkSection(doc: ParsedMarkdown, node: FolderNode): Violation[];
|
|
9
|
+
export declare function checkAcceptanceCriteria(doc: ParsedMarkdown, node: FolderNode, pack?: LanguagePack | null): Violation[];
|
|
10
|
+
export declare function validateFormat(doc: ParsedMarkdown, markerType: MarkerType, config: TaprootConfig, node?: FolderNode, pack?: LanguagePack | null): Violation[];
|