faf-taf-git 2.3.1 → 2.4.1

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.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { TestResults } from './jest';
7
7
  export { TestResults } from './jest';
8
+ export { parseJunitOutput } from './junit';
8
9
  export { parseWjttcOutput } from './wjttc';
9
10
  export declare const SUPPORTED_PARSERS: readonly string[];
10
11
  /** What the CLI and the Action report when no parser matches. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["file:///Users/wolfejam/FAF/faf-taf-git/src/parsers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAMrC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAgB3C,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAkC,CAAC;AAElF,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,QAA6E,CAAC;AAE7G;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["file:///Users/wolfejam/FAF/faf-taf-git/src/parsers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAOrC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAmB3C,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAkC,CAAC;AAElF,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,QAA6E,CAAC;AAE7G;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMlE"}
@@ -5,22 +5,28 @@
5
5
  * Tries each framework parser in order until one succeeds.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.UNPARSEABLE_OUTPUT = exports.SUPPORTED_PARSERS = exports.parseWjttcOutput = void 0;
8
+ exports.UNPARSEABLE_OUTPUT = exports.SUPPORTED_PARSERS = exports.parseWjttcOutput = exports.parseJunitOutput = void 0;
9
9
  exports.parseTestOutput = parseTestOutput;
10
10
  const bun_1 = require("./bun");
11
11
  const jest_1 = require("./jest");
12
+ const junit_1 = require("./junit");
12
13
  const vitest_1 = require("./vitest");
13
14
  const wjttc_1 = require("./wjttc");
15
+ var junit_2 = require("./junit");
16
+ Object.defineProperty(exports, "parseJunitOutput", { enumerable: true, get: function () { return junit_2.parseJunitOutput; } });
14
17
  var wjttc_2 = require("./wjttc");
15
18
  Object.defineProperty(exports, "parseWjttcOutput", { enumerable: true, get: function () { return wjttc_2.parseWjttcOutput; } });
16
19
  /**
17
20
  * Every parser, in the order parseTestOutput tries them. Order matters:
18
- * 1. Bun — `Ran N tests across M files` is bun-unique.
19
- * 2. WJTTC — `pc-ai WJTTC` / suite `pc-ai-wjttc` (FAF bar; not Jest-shaped).
20
- * 3. Jest — `Tests: … total`.
21
- * 4. Vitest — `Tests … (N)`.
21
+ * 1. JUnit XML — a `<testsuite>` element; first, because a report's captured
22
+ * stdout can hold another runner's summary line.
23
+ * 2. Bun — `Ran N tests across M files` is bun-unique.
24
+ * 3. WJTTC — `pc-ai WJTTC` / suite `pc-ai-wjttc` (FAF bar; not Jest-shaped).
25
+ * 4. Jest — `Tests: … total`.
26
+ * 5. Vitest — `Tests … (N)`.
22
27
  */
23
28
  const PARSERS = [
29
+ ['JUnit XML', junit_1.parseJunitOutput],
24
30
  ['Bun', bun_1.parseBunOutput],
25
31
  ['WJTTC', wjttc_1.parseWjttcOutput],
26
32
  ['Jest', jest_1.parseJestOutput],
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAkCH,0CAMC;AArCD,+BAAuC;AACvC,iCAAyC;AACzC,qCAA6C;AAC7C,mCAA2C;AAG3C,iCAA2C;AAAlC,yGAAA,gBAAgB,OAAA;AAEzB;;;;;;GAMG;AACH,MAAM,OAAO,GAA0F;IACrG,CAAC,KAAK,EAAE,oBAAc,CAAC;IACvB,CAAC,OAAO,EAAE,wBAAgB,CAAC;IAC3B,CAAC,MAAM,EAAE,sBAAe,CAAC;IACzB,CAAC,QAAQ,EAAE,0BAAiB,CAAC;CAC9B,CAAC;AAEW,QAAA,iBAAiB,GAAsB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AAElF,iEAAiE;AACpD,QAAA,kBAAkB,GAAG,2CAA2C,yBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAE7G;;;GAGG;AACH,SAAgB,eAAe,CAAC,MAAc;IAC5C,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAuCH,0CAMC;AA1CD,+BAAuC;AACvC,iCAAyC;AACzC,mCAA2C;AAC3C,qCAA6C;AAC7C,mCAA2C;AAG3C,iCAA2C;AAAlC,yGAAA,gBAAgB,OAAA;AACzB,iCAA2C;AAAlC,yGAAA,gBAAgB,OAAA;AAEzB;;;;;;;;GAQG;AACH,MAAM,OAAO,GAA0F;IACrG,CAAC,WAAW,EAAE,wBAAgB,CAAC;IAC/B,CAAC,KAAK,EAAE,oBAAc,CAAC;IACvB,CAAC,OAAO,EAAE,wBAAgB,CAAC;IAC3B,CAAC,MAAM,EAAE,sBAAe,CAAC;IACzB,CAAC,QAAQ,EAAE,0BAAiB,CAAC;CAC9B,CAAC;AAEW,QAAA,iBAAiB,GAAsB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AAElF,iEAAiE;AACpD,QAAA,kBAAkB,GAAG,2CAA2C,yBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAE7G;;;GAGG;AACH,SAAgB,eAAe,CAAC,MAAc;IAC5C,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * JUnit XML parser
3
+ *
4
+ * Extracts test counts from a JUnit XML report — the common format nearly every
5
+ * test runner can write: pytest (--junitxml), cargo (nextest, cargo2junit), Go
6
+ * (go-junit-report, gotestsum), Maven/Gradle, .NET, PHPUnit, Jest (jest-junit).
7
+ */
8
+ import { TestResults } from './jest';
9
+ /**
10
+ * Parse a JUnit XML report to extract test results
11
+ *
12
+ * Handles:
13
+ * - a <testsuites> root that carries the totals (gotestsum, Maven)
14
+ * - a <testsuites> root without totals, around counted <testsuite>s (pytest)
15
+ * - a single <testsuite> root
16
+ * - nested <testsuite>s (only the outermost level is counted, never twice)
17
+ * - suites with no counts at all (the <testcase> elements are counted instead)
18
+ *
19
+ * failures + errors count as failed; skipped + disabled count as skipped.
20
+ */
21
+ export declare function parseJunitOutput(output: string): TestResults | null;
22
+ //# sourceMappingURL=junit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"junit.d.ts","sourceRoot":"","sources":["file:///Users/wolfejam/FAF/faf-taf-git/src/parsers/junit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAyCrC;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAgDnE"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * JUnit XML parser
4
+ *
5
+ * Extracts test counts from a JUnit XML report — the common format nearly every
6
+ * test runner can write: pytest (--junitxml), cargo (nextest, cargo2junit), Go
7
+ * (go-junit-report, gotestsum), Maven/Gradle, .NET, PHPUnit, Jest (jest-junit).
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.parseJunitOutput = parseJunitOutput;
11
+ const SUITE_TAG = /<(\/?)testsuite\b([^>]*?)(\/?)>/g;
12
+ /** Read one numeric attribute; undefined when the attribute is absent. */
13
+ function attr(attrs, name) {
14
+ const match = attrs.match(new RegExp(`\\b${name}\\s*=\\s*["'](\\d+)["']`));
15
+ return match ? parseInt(match[1], 10) : undefined;
16
+ }
17
+ /** Counts from a <testsuite> or <testsuites> attribute list, or null without a `tests` count. */
18
+ function countsFromAttrs(attrs) {
19
+ const tests = attr(attrs, 'tests');
20
+ if (tests === undefined)
21
+ return null;
22
+ return {
23
+ tests,
24
+ failed: (attr(attrs, 'failures') ?? 0) + (attr(attrs, 'errors') ?? 0),
25
+ skipped: (attr(attrs, 'skipped') ?? 0) + (attr(attrs, 'disabled') ?? 0),
26
+ };
27
+ }
28
+ /** Fallback when suites carry no counts: walk the <testcase> elements themselves. */
29
+ function countsFromTestcases(xml) {
30
+ const counts = { tests: 0, failed: 0, skipped: 0 };
31
+ const testcase = /<testcase\b[^>]*?(?:\/>|>([\s\S]*?)<\/testcase>)/g;
32
+ let match;
33
+ while ((match = testcase.exec(xml)) !== null) {
34
+ counts.tests++;
35
+ const body = match[1] ?? '';
36
+ if (/<(failure|error)\b/.test(body))
37
+ counts.failed++;
38
+ else if (/<skipped\b/.test(body))
39
+ counts.skipped++;
40
+ }
41
+ return counts;
42
+ }
43
+ /**
44
+ * Parse a JUnit XML report to extract test results
45
+ *
46
+ * Handles:
47
+ * - a <testsuites> root that carries the totals (gotestsum, Maven)
48
+ * - a <testsuites> root without totals, around counted <testsuite>s (pytest)
49
+ * - a single <testsuite> root
50
+ * - nested <testsuite>s (only the outermost level is counted, never twice)
51
+ * - suites with no counts at all (the <testcase> elements are counted instead)
52
+ *
53
+ * failures + errors count as failed; skipped + disabled count as skipped.
54
+ */
55
+ function parseJunitOutput(output) {
56
+ const xml = output.replace(/<!--[\s\S]*?-->/g, '');
57
+ if (!/<testsuites?\b/.test(xml))
58
+ return null;
59
+ let counts = null;
60
+ const root = xml.match(/<testsuites\b([^>]*)>/);
61
+ if (root)
62
+ counts = countsFromAttrs(root[1]);
63
+ if (!counts) {
64
+ // Sum the outermost <testsuite>s; a suite without counts sends us to the testcases.
65
+ let depth = 0;
66
+ let summed = { tests: 0, failed: 0, skipped: 0 };
67
+ let seen = 0;
68
+ let match;
69
+ SUITE_TAG.lastIndex = 0;
70
+ while ((match = SUITE_TAG.exec(xml)) !== null) {
71
+ const [, closing, attrs, selfClosing] = match;
72
+ if (closing) {
73
+ depth--;
74
+ continue;
75
+ }
76
+ if (depth === 0 && summed) {
77
+ seen++;
78
+ const suite = countsFromAttrs(attrs);
79
+ if (suite) {
80
+ summed.tests += suite.tests;
81
+ summed.failed += suite.failed;
82
+ summed.skipped += suite.skipped;
83
+ }
84
+ else {
85
+ summed = null;
86
+ }
87
+ }
88
+ if (!selfClosing)
89
+ depth++;
90
+ }
91
+ counts = seen > 0 && summed ? summed : countsFromTestcases(xml);
92
+ }
93
+ if (counts.tests === 0)
94
+ return null;
95
+ const passed = Math.max(0, counts.tests - counts.failed - counts.skipped);
96
+ return {
97
+ total: counts.tests,
98
+ passed,
99
+ failed: counts.failed,
100
+ skipped: counts.skipped > 0 ? counts.skipped : undefined,
101
+ result: counts.failed > 0 ? 'FAILED' : 'PASSED',
102
+ };
103
+ }
104
+ //# sourceMappingURL=junit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"junit.js","sourceRoot":"","sources":["../../src/parsers/junit.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "faf-taf-git",
3
- "version": "2.3.1",
4
- "description": "A Test Receipt Printer for git. Every CI run prints a receipt to .taf — append-only, timestamped, cannot be gamed. Proof over time.",
3
+ "version": "2.4.1",
4
+ "description": "A Test Receipt Printer for git. Every CI run prints a receipt to .taf — append-only, timestamped, kept in git. Proof over time.",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
7
7
  "faf-taf-git": "dist/cli.js"
@@ -36,11 +36,11 @@
36
36
  "yaml": "^2.3.4"
37
37
  },
38
38
  "devDependencies": {
39
- "@types/jest": "^29.5.11",
39
+ "@types/jest": "^30.0.0",
40
40
  "@types/node": "^20.10.6",
41
41
  "@vercel/ncc": "^0.38.1",
42
- "jest": "^29.7.0",
43
- "ts-jest": "^29.1.1",
42
+ "jest": "^30.5.2",
43
+ "ts-jest": "^29.4.15",
44
44
  "typescript": "^5.3.3"
45
45
  },
46
46
  "overrides": {
package/project.faf CHANGED
@@ -1,255 +1,57 @@
1
- faf_version: 2.5.0
2
- ai_scoring_system: 2025-09-20
3
- ai_score: 65%
4
- ai_confidence: MODERATE
5
- ai_value: 30_seconds_replaces_20_minutes_of_questions
6
- ai_tldr:
7
- project: faf-taf-git v2.1.1 - A Test Receipt Printer for git. Proof over time, cannot be gamed.
8
- stack: TypeScript CLI + GitHub Action (pre-capture architecture)
9
- quality_bar: ZERO_ERRORS_F1_STANDARDS
10
- status: PRODUCTION_READY (working in faf-cli repo)
11
- breakthrough: v2.0.0 pre-capture pattern (tested 19 methods before this)
12
- current_focus: Ecosystem adoption and user onboarding
13
- your_role: Support users, fix bugs, add test framework support
14
- instant_context:
15
- what_building: A Test Receipt Printer for git. CLI runs on any CI/CD; prints append-only test receipts to .taf, kept in git. FAF-aware optionally — receipts can carry FAF score/identity, but TAF works on any repo.
16
- tech_stack: TypeScript
17
- main_language: TypeScript
18
- deployment: npm + GitHub Actions Marketplace
19
- architecture: v2.0.0 pre-capture pattern (separate test execution from receipt generation)
20
- use_cases: Standalone CLI, GitHub Action, GitLab CI, Jenkins, CircleCI, Local development
21
- test_support: Jest (fully supported) with plans for Mocha, Vitest, Pytest, Go test, Rust cargo
22
- golden_triangle: .faf (project DNA) + repo (code) + .taf (test proof)
23
- key_files:
24
- - src/index.ts (v2.0.0 rewrite - reads test output file)
25
- - src/parsers/jest.ts (v2.0.4 fix - correct pattern matching)
26
- - src/taf-core.ts (MCP-portable core)
27
- - action.yml (v2.0.2 fix - correct bundle path)
28
- - README.md (6Ws format)
29
- - CHANGELOG.md (22 versions documented)
30
- documentation:
31
- - README: 6Ws format (What/Who/Why/Where/When/How)
32
- - CHANGELOG: Complete debugging journey (v1.0.4 → v2.0.4)
33
- - Migration guide: v1.x → v2.x breaking changes
34
- context_quality:
35
- slots_filled: 11/21 (52%)
36
- ai_confidence: MODERATE
37
- handoff_ready: true
38
- missing_context: []
1
+ faf_version: "3.0"
39
2
  project:
40
3
  name: faf-taf-git
41
- goal: Platform-agnostic CLI + GitHub Action for TAF updates
42
- innovation: On-the-fly Testing Audit Files - Unheard of in CI/CD
43
- taf_meaning:
44
- file: Testing Audit File (.taf)
45
- feed: Testing Activity Feed (live updates)
4
+ goal: "A Test Receipt Printer for git. Every CI run prints a receipt to .taf: append-only, timestamped, kept in git. Proof over time."
46
5
  main_language: TypeScript
47
- type: cli
48
- generated: 2025-10-28T01:26:05.656Z
49
- repository: https://github.com/Wolfe-Jam/faf-taf-git
50
- license: MIT
51
- author: wolfejam
52
- keywords: testing, taf, ci-cd, github-actions, gitlab-ci, platform-agnostic, jest, faf, git
53
- mission: 🚀 Make Your AI Happy! 🧡 Trust-Driven 🤖
54
- revolution: 30 seconds replaces 20 minutes of questions
55
- brand: F1-Inspired Software Engineering - Championship AI Context
56
- ai_instructions:
57
- priority_order:
58
- - 1. Read THIS .faf file first
59
- - 2. Check CLAUDE.md for session context
60
- - 3. Review package.json for dependencies
61
- working_style:
62
- code_first: true
63
- explanations: minimal
64
- quality_bar: zero_errors
65
- testing: required
66
- warnings:
67
- - Never modify dial components without approval
68
- - All TypeScript must pass strict mode
69
- - Test coverage required for new features
6
+ type: cli # found: package.json bin
70
7
  stack:
71
8
  frontend: slotignored
72
9
  css_framework: slotignored
73
10
  ui_library: slotignored
74
11
  state_management: slotignored
75
12
  backend: slotignored
76
- runtime: Node.js
13
+ api_type: slotignored
14
+ runtime: slotignored
77
15
  database: slotignored
78
16
  connection: slotignored
79
- build: TypeScript Compiler
80
- package_manager: npm
81
- api_type: REST
82
- hosting: npm Registry
17
+ hosting: npm registry + GitHub Actions Marketplace
18
+ build: TypeScript (tsc)
83
19
  cicd: GitHub Actions
84
- targetUser: developers
85
- coreProblem: understanding project
86
- missionPurpose: project documentation
87
- packageManager: npm/yarn/pnpm
88
- mainLanguage: JavaScript/TypeScript
89
- cli_features:
90
- commands:
91
- - npx faf-taf-git
92
- - --file (test output file - v2.0.0+)
93
- - --command (custom test command - legacy v1.x)
94
- - --commit (auto-commit .taf changes)
95
- - --verbose (detailed output)
96
- - --message (custom commit message)
97
- - --cwd (working directory)
98
- github_action_inputs:
99
- test-output-file: required (v2.0.0+)
100
- auto-commit: true (default)
101
- commit-message: "chore(taf): update .taf receipt [skip ci] (default)"
102
- architecture:
103
- version: v2.0.0
104
- pattern: pre-capture
105
- breakthrough: Separate test execution from receipt generation
106
- why: Avoids GitHub Actions execution context corruption
107
- reliability: Tested 19 methods before this solution
108
- benefits:
109
- - More reliable (no execution context issues)
110
- - Platform-agnostic (works everywhere)
111
- - Simpler (400+ lines removed, 11KB smaller)
112
- test_frameworks:
113
- - Jest (fully supported)
114
- supported_platforms:
115
- - GitHub Actions
116
- - GitLab CI
117
- - Bitbucket Pipelines
118
- - Jenkins
119
- - CircleCI
120
- - Travis CI
121
- - Azure Pipelines
122
- - Local development
123
- taf_format:
124
- format: YAML
125
- file_location: .taf (hidden dotfile)
126
- purpose: Git-tracked testing timeline - permanent proof your code works
127
- links_to_faf: true
128
- snapshots_faf_score: true
129
- closed_loop_system: .faf (DNA) + repo (code) + .taf (proof) = Golden Triangle
130
- deployment:
131
- npm_package: faf-taf-git@2.1.1
132
- github_action: Wolfe-Jam/faf-taf-git@v2.1.1
133
- marketplaces:
134
- - npm Registry
135
- - GitHub Actions Marketplace
136
- install: npx faf-taf-git
137
- production_users:
138
- - faf-cli (working in CI)
139
- testing:
140
- framework: Jest
141
- coverage: Core functionality
142
- ci_integration: GitHub Actions
143
- test_files:
144
- - tests/ (unit tests)
145
- quality_gates:
146
- - All tests must pass
147
- - TypeScript strict mode
148
- - Zero build errors
149
- examples:
150
- github_actions: README.md Quick Start section
151
- standalone_cli: README.md Standalone CLI section
152
- gitlab_ci: README.md GitLab CI section
153
- jenkins: README.md Jenkins section
154
- migration: README.md Migration from v1.x section
155
- performance:
156
- bundle_size: 1.2MB (v2.0.0 - reduced 11KB from v1.x)
157
- startup_time: <100ms
158
- parse_time: <10ms for typical Jest output
159
- reliability: 100% success rate in faf-cli CI (after v2.0.4)
160
- preferences:
161
- quality_bar: zero_errors
162
- commit_style: conventional_emoji
163
- response_style: concise_code_first
164
- explanation_level: minimal
165
- communication: direct
166
- testing: required
167
- documentation: as_needed
168
- contributing:
169
- guidelines: CONTRIBUTING.md
170
- code_style: TypeScript strict mode
171
- pr_process: Fork, branch, PR with tests
172
- commit_format: Conventional commits with Co-Authored-By
173
- testing_required: true
174
- documentation_required: For new features
175
- resources:
176
- readme: README.md (6Ws format)
177
- changelog: CHANGELOG.md (22 versions documented)
178
- license: MIT
179
- repository: https://github.com/Wolfe-Jam/faf-taf-git
180
- npm: https://npmjs.com/package/faf-taf-git
181
- issues: https://github.com/Wolfe-Jam/faf-taf-git/issues
182
- discussions: https://github.com/Wolfe-Jam/faf/discussions
183
- state:
184
- phase: production
185
- version: 2.3.1
186
- focus: ecosystem_adoption
187
- status: production_ready
188
- achievements:
189
- - v2.0.0 architectural redesign (pre-capture pattern)
190
- - v2.0.4 parser fix (working in CI)
191
- - Updated README (6Ws format)
192
- - Updated CHANGELOG (22 versions documented)
193
- - TAF Receipt generating successfully in faf-cli repo
194
- next_milestone: user_adoption
195
- blockers: null
196
- tags:
197
- auto_generated:
198
- - faf-taf-action
199
- - none
200
- - typescript
201
- smart_defaults:
202
- - .faf
203
- - ai-ready
204
- - "2025"
205
- - software
206
- - open-source
207
- user_defined: null
20
+ monorepo_tool: slotignored
21
+ package_manager: slotignored
22
+ workspaces: slotignored
23
+ admin: slotignored
24
+ cache: slotignored
25
+ search: slotignored
26
+ storage: slotignored
208
27
  human_context:
209
- who: Development teams adopting test-receipt provenance — open-source maintainers proving honest test history, regulated industries (SOC 2 / ISO / HIPAA) needing append-only audit trails, supply-chain verifiers proving tests ran before artifacts shipped, AI-agent reviewers verifying agent code passes tests consistently, and FAF-ecosystem projects completing the CAR Framework (Claim → Audit → Receipt).
210
- what: TAF file updates in CI/CD pipelines
211
- why: Improve development efficiency
212
- where: Any CI/CD platform (GitHub, GitLab, Bitbucket)
213
- when: During CI/CD test runs
214
- how: Test-driven development
215
- additional_context: Part of Golden Triangle (.faf + repo + .taf). Tracks test runs permanently in git. Supports Jest, works as CLI or GitHub Action. Platform-agnostic core - runs on ANY CI/CD with Node.js.
216
- context_score: 0
217
- total_prd_score: 65
218
- success_rate: 50%
219
- scores:
220
- faf_score: 50
221
- slot_based_percentage: 52
222
- ai_score: 65
223
- total_slots: 21
224
- filled_slots: 11
225
- scoring_method: Honest percentage - no fake minimums
226
- trust_embedded: COUNT ONCE architecture - trust MY embedded scores
227
- ai_scoring_details:
228
- system_date: 2025-09-20
229
- slot_based_percentage: 52
230
- ai_score: 65
231
- total_slots: 21
232
- filled_slots: 11
233
- scoring_method: Honest percentage - no fake minimums
234
- trust_embedded: COUNT ONCE architecture - trust MY embedded scores
235
- faf_dna:
236
- birth_dna: 22
237
- birth_certificate: FAF-2025-FAFTAFAC-TMZU
238
- birth_date: 2025-10-28T01:26:05.692Z
239
- current_score: 50
240
- projectName: faf-taf-git
241
- projectGoal: A Test Receipt Printer for git — append-only test receipts on every CI run. Proof over time, cannot be gamed.
242
- generated: 2026-02-17T20:14:09.215Z
243
- last_major_update: v2.0.4 (2026-02-17)
244
- production_status: Working in faf-cli repository
245
- stack_signature: typescript-nodejs-javascript
246
- detected_frameworks:
247
- - Claude Code
248
- - Jest
249
- - Node.js
250
- - JavaScript
28
+ who: Maintainers and teams who want proof their tests kept passing on main, and the reviewers, auditors and AI agents who check it
29
+ what: "A Test Receipt Printer for git. Every CI run prints a receipt to .taf: append-only, timestamped, kept in git. Proof over time."
30
+ why: Test results vanish when CI logs expire. Receipts stay in git, so anyone can look back and check whether main was green before a release.
31
+ where: https://github.com/Wolfe-Jam/faf-taf-git
32
+ when: v2.4.1 (2026-10-30), --version fix; v2.4.0 (2026-10-29) JUnit XML parser; live since v1.0.0 (2025-10-27)
33
+ how: "Run tests, save the output (or a JUnit XML report), then `uses: Wolfe-Jam/faf-taf-git@v2.4.1` with `target-branch: taf-receipts`. Anywhere else: `npx faf-taf-git --file test-output.txt --commit`. The taf-receipts branch only grows: never rewrite it."
34
+ monorepo:
35
+ packages_count: slotignored
36
+ build_orchestrator: slotignored
37
+ versioning_strategy: slotignored
38
+ shared_configs: slotignored
39
+ remote_cache: slotignored
40
+ tech_stack:
251
41
  - TypeScript
252
- turbo_cat_intelligence: 260
253
- stats:
254
- last_auto_time: 0.6
255
- times_run: 1
42
+ - "@actions/core"
43
+ - "@actions/exec"
44
+ - "@actions/github"
45
+ - yaml
46
+ key_files:
47
+ - package.json
48
+ - src/index.ts
49
+ - src/cli.ts
50
+ - src/parsers/
51
+ - action.yml
52
+ - RECEIPT-FORMAT.md
53
+ - README.md
54
+ - tsconfig.json
55
+ commands:
56
+ build: npm run build
57
+ test: npm run test
package/src/cli.ts CHANGED
@@ -222,12 +222,26 @@ function handleBadgeCommand(args: string[]): void {
222
222
  }
223
223
  }
224
224
 
225
+ /**
226
+ * The installed package's version. package.json sits one level above both
227
+ * src/ (tests) and dist/ (the published bin).
228
+ */
229
+ export function cliVersion(): string {
230
+ return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8')).version;
231
+ }
232
+
225
233
  /**
226
234
  * CLI entry point
227
235
  */
228
236
  async function main() {
229
237
  const args = process.argv.slice(2);
230
238
 
239
+ // --version / -V (-v is --verbose)
240
+ if (args.includes('--version') || args.includes('-V')) {
241
+ console.log(cliVersion());
242
+ return;
243
+ }
244
+
231
245
  // Subcommand: badge
232
246
  if (args[0] === 'badge') {
233
247
  handleBadgeCommand(args.slice(1));
@@ -236,7 +250,7 @@ async function main() {
236
250
 
237
251
  // Show help
238
252
  if (args.includes('--help') || args.includes('-h')) {
239
- console.log(`\nfaf-taf-git - The Git-Native Receipt Printer\n\nUSAGE:\n npm test 2>&1 | tee test-output.txt\n npx faf-taf-git --file test-output.txt [OPTIONS]\n\n # WJTTC / pc-ai bar\n node tests/wjttc/run-wjttc.mjs 2>&1 | tee wjttc-out.txt\n npx faf-taf-git --file wjttc-out.txt\n\nCOMMANDS:\n badge Generate a shields.io-style SVG badge from .taf\n\nOPTIONS:\n --file <path> Path to test output file (required)\n --commit Auto-commit .taf changes to git\n --message <msg> Custom commit message\n --cwd <dir> Working directory (default: current)\n --verbose, -v Verbose output\n --help, -h Show this help\n\nPARSERS:\n Bun, WJTTC (pc-ai bar), Jest, Vitest\n\nEXAMPLES:\n npm test 2>&1 | tee test-output.txt\n npx faf-taf-git --file test-output.txt\n\n npx faf-taf-git --file test-output.txt --commit\n\n npx faf-taf-git badge --output badge.svg\n\nPLATFORM SUPPORT:\n Works in ANY CI/CD that runs Node.js:\n GitHub Actions, GitLab CI, Jenkins, CircleCI,\n Bitbucket Pipelines, Azure Pipelines, local dev\n\nLEARN MORE:\n https://github.com/Wolfe-Jam/faf-taf-git\n`);
253
+ console.log(`\nfaf-taf-git - The Git-Native Receipt Printer\n\nUSAGE:\n npm test 2>&1 | tee test-output.txt\n npx faf-taf-git --file test-output.txt [OPTIONS]\n\n # WJTTC / pc-ai bar\n node tests/wjttc/run-wjttc.mjs 2>&1 | tee wjttc-out.txt\n npx faf-taf-git --file wjttc-out.txt\n\nCOMMANDS:\n badge Generate a shields.io-style SVG badge from .taf\n\nOPTIONS:\n --file <path> Path to test output file (required)\n --commit Auto-commit .taf changes to git\n --message <msg> Custom commit message\n --cwd <dir> Working directory (default: current)\n --verbose, -v Verbose output\n --version, -V Print the version\n --help, -h Show this help\n\nPARSERS:\n JUnit XML (pytest, cargo, Go, Java, .NET ...), Bun, WJTTC (pc-ai bar), Jest, Vitest\n\nEXAMPLES:\n npm test 2>&1 | tee test-output.txt\n npx faf-taf-git --file test-output.txt\n\n npx faf-taf-git --file test-output.txt --commit\n\n npx faf-taf-git badge --output badge.svg\n\nPLATFORM SUPPORT:\n Works in ANY CI/CD that runs Node.js:\n GitHub Actions, GitLab CI, Jenkins, CircleCI,\n Bitbucket Pipelines, Azure Pipelines, local dev\n\nLEARN MORE:\n https://github.com/Wolfe-Jam/faf-taf-git\n`);
240
254
  process.exit(0);
241
255
  }
242
256
 
package/src/index.ts CHANGED
@@ -149,6 +149,7 @@ async function run(): Promise<void> {
149
149
  core.setOutput('passed', testResults.passed.toString());
150
150
  core.setOutput('failed', testResults.failed.toString());
151
151
  core.setOutput('total', testResults.total.toString());
152
+ core.setOutput('skipped', (testResults.skipped ?? 0).toString());
152
153
 
153
154
  // If target-branch is set, save .taf seed and switch branches
154
155
  let tafSeed: string | null = null;
@@ -7,20 +7,25 @@
7
7
  import { TestResults } from './jest';
8
8
  import { parseBunOutput } from './bun';
9
9
  import { parseJestOutput } from './jest';
10
+ import { parseJunitOutput } from './junit';
10
11
  import { parseVitestOutput } from './vitest';
11
12
  import { parseWjttcOutput } from './wjttc';
12
13
 
13
14
  export { TestResults } from './jest';
15
+ export { parseJunitOutput } from './junit';
14
16
  export { parseWjttcOutput } from './wjttc';
15
17
 
16
18
  /**
17
19
  * Every parser, in the order parseTestOutput tries them. Order matters:
18
- * 1. Bun — `Ran N tests across M files` is bun-unique.
19
- * 2. WJTTC — `pc-ai WJTTC` / suite `pc-ai-wjttc` (FAF bar; not Jest-shaped).
20
- * 3. Jest — `Tests: … total`.
21
- * 4. Vitest — `Tests … (N)`.
20
+ * 1. JUnit XML — a `<testsuite>` element; first, because a report's captured
21
+ * stdout can hold another runner's summary line.
22
+ * 2. Bun — `Ran N tests across M files` is bun-unique.
23
+ * 3. WJTTC — `pc-ai WJTTC` / suite `pc-ai-wjttc` (FAF bar; not Jest-shaped).
24
+ * 4. Jest — `Tests: … total`.
25
+ * 5. Vitest — `Tests … (N)`.
22
26
  */
23
27
  const PARSERS: ReadonlyArray<readonly [name: string, parse: (output: string) => TestResults | null]> = [
28
+ ['JUnit XML', parseJunitOutput],
24
29
  ['Bun', parseBunOutput],
25
30
  ['WJTTC', parseWjttcOutput],
26
31
  ['Jest', parseJestOutput],