azcodr 1.5.1 → 2.0.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/.agents/hooks.json.example +42 -42
- package/.agents/scripts/safety_guard.sh +143 -34
- package/.agents/scripts/verify_completion.sh +90 -27
- package/.agents/skills/agentic-architect/SKILL.md +125 -125
- package/.agents/skills/agentic-architect/references/agents_md_template.md +62 -62
- package/.agents/skills/agentic-architect/references/refinement_workflow.md +32 -32
- package/.agents/skills/agentic-architect/references/skill_architecture_inquiry.md +63 -63
- package/.agents/skills/agentic-architect/references/skill_template.md +56 -56
- package/.agents/skills/agentic-architect/scripts/validate_agentic_configs.sh +402 -401
- package/.agents/skills/clean-code-refactor/SKILL.md +91 -91
- package/.agents/skills/clean-code-refactor/references/clean_code_smells.md +27 -27
- package/.agents/skills/clean-code-refactor/references/design_patterns_ts.md +65 -65
- package/.agents/skills/compliance-audit/SKILL.md +120 -120
- package/.agents/skills/compliance-audit/references/owasp_top10_controls.md +16 -16
- package/.agents/skills/compliance-audit/references/soc2_iso_controls.md +28 -28
- package/.agents/skills/lets-build/SKILL.md +173 -173
- package/.agents/skills/lets-build/references/architecture_interview_matrix.md +115 -115
- package/.agents/skills/lets-build/references/hexagonal_bootstrap_scaffolds.md +160 -160
- package/.agents/skills/lets-build/references/project_readme_template.md +79 -79
- package/.agents/skills/lets-build/scripts/bootstrap_workspace.sh +419 -255
- package/.agents/skills/product-analyst/SKILL.md +154 -154
- package/.agents/skills/product-analyst/references/backlog_ordering_techniques.md +107 -107
- package/.agents/skills/product-analyst/references/gherkin_patterns.md +46 -46
- package/.agents/skills/product-analyst/references/invest_checklist.md +38 -38
- package/.agents/skills/product-analyst/references/okr_alignment_guide.md +76 -76
- package/.agents/skills/product-analyst/references/smart_tasks.md +59 -59
- package/.agents/skills/relentless-questioner/SKILL.md +128 -128
- package/.agents/skills/relentless-questioner/references/adaptive_question_trees.md +102 -102
- package/.editorconfig +19 -19
- package/.github/workflows/ci.yml +167 -56
- package/.github/workflows/publish.yml +200 -0
- package/.gitignore +40 -25
- package/AGENTS.md +103 -102
- package/LICENSE +21 -21
- package/README.md +168 -154
- package/bin/azcodr.js +14 -228
- package/docs/knowledge/ubiquitous_language.md +31 -18
- package/docs/rules/agentic_configuration.md +259 -259
- package/docs/rules/api_architecture.md +179 -179
- package/docs/rules/authentication.md +76 -76
- package/docs/rules/authorization.md +75 -75
- package/docs/rules/caching.md +69 -69
- package/docs/rules/clean_code.md +62 -62
- package/docs/rules/cloud_native.md +41 -41
- package/docs/rules/cqrs.md +203 -203
- package/docs/rules/database_design.md +125 -125
- package/docs/rules/database_operations.md +69 -69
- package/docs/rules/design_patterns.md +98 -98
- package/docs/rules/devops_ci_cd.md +76 -76
- package/docs/rules/domain_driven_design.md +122 -122
- package/docs/rules/error_handling.md +54 -52
- package/docs/rules/feature_flags.md +59 -59
- package/docs/rules/frontend_architecture.md +157 -157
- package/docs/rules/multitenancy_architecture.md +98 -98
- package/docs/rules/product_ownership.md +127 -127
- package/docs/rules/project_management.md +49 -49
- package/docs/rules/relentless_questioning.md +52 -52
- package/docs/rules/requirements_engineering.md +98 -98
- package/docs/rules/security_compliance.md +53 -53
- package/docs/rules/server_driven_ui.md +88 -88
- package/docs/rules/test_driven_development.md +185 -185
- package/docs/rules/transactional_email.md +27 -27
- package/docs/rules/type_safety.md +65 -65
- package/docs/rules/ui_ux_architecture.md +150 -150
- package/docs/rules/workflow_state_machines.md +117 -117
- package/lib/cli-parse.js +51 -0
- package/lib/cli-target.js +109 -0
- package/lib/cli.js +180 -0
- package/lib/errors.js +28 -0
- package/lib/git.js +29 -0
- package/lib/guards.js +96 -0
- package/lib/index.d.ts +199 -134
- package/lib/index.js +5 -5
- package/lib/links.js +123 -0
- package/lib/permissions.js +44 -0
- package/lib/repo.js +90 -0
- package/lib/scaffold.js +238 -399
- package/memory.md +119 -36
- package/package.json +65 -62
- package/scripts/test_coverage.js +66 -38
- package/scripts/validate/adr.js +151 -0
- package/scripts/validate/io.js +84 -0
- package/scripts/validate/links.js +167 -0
- package/scripts/validate/parity.js +124 -0
- package/scripts/validate/root.js +184 -0
- package/scripts/validate/rules.js +44 -0
- package/scripts/validate/skills.js +96 -0
- package/scripts/validate/text.js +29 -0
- package/scripts/validate-cli.js +13 -0
- package/scripts/validate.js +112 -218
- package/.github/copilot-instructions.md +0 -1
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
# Adaptive Questioning Trees & Contextual Branching Matrices
|
|
2
|
-
|
|
3
|
-
> **Core Purpose:** Detailed decision trees for the `relentless-questioner` skill, demonstrating how subsequent questions adapt dynamically based on previous user responses.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Decision Tree 1: Mutating Operations & State Changes
|
|
8
|
-
|
|
9
|
-
```mermaid
|
|
10
|
-
flowchart TD
|
|
11
|
-
Q1["Does the operation mutate database state?"]
|
|
12
|
-
Q1 -->|Yes| Q2["Does it involve multiple tables, monetary balances, or inventory?"]
|
|
13
|
-
Q1 -->|No / Read Only| Q_Read["Branch: Read Performance & Consistency"]
|
|
14
|
-
|
|
15
|
-
Q2 -->|Yes: Financial / Inventory| Q_Acid["1. Transaction Isolation: REPEATABLE READ or SERIALIZABLE?\n2. Lock Ordering: How to prevent deadlocks?\n3. Concurrency: Optimistic Concurrency Control (version) or pessimistic locking?"]
|
|
16
|
-
Q2 -->|No: Standard Entity CRUD| Q_Crud["1. Soft delete or hard delete?\n2. Unique constraints across tenant?\n3. Cascading relations?"]
|
|
17
|
-
|
|
18
|
-
Q_Acid --> Q3["Does the mutation emit domain events or notify external systems?"]
|
|
19
|
-
Q_Crud --> Q3
|
|
20
|
-
|
|
21
|
-
Q3 -->|Yes| Q_Outbox["How is the dual-write avoided?\n(Enforce Transactional Outbox pattern before broker publish)"]
|
|
22
|
-
Q3 -->|No| Q4["Idempotency: Is an Idempotency-Key header required to guard against network retries?"]
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Decision Tree 2: Multi-Tenancy & Authorization Boundaries
|
|
28
|
-
|
|
29
|
-
```mermaid
|
|
30
|
-
flowchart TD
|
|
31
|
-
Q1["Who executes this action and across which boundary?"]
|
|
32
|
-
Q1 -->|End User via Web/API| Q_Auth["1. What roles are permitted (ADMIN, MEMBER, CUSTOMER)?\n2. Are dynamic ABAC attributes involved (e.g. order value threshold)?\n3. Can a user act across multiple tenants (switch tenant)?"]
|
|
33
|
-
Q1 -->|System / Background Job| Q_Worker["1. How is tenant context established without an HTTP session?\n2. What service principal / token credentials are used?"]
|
|
34
|
-
|
|
35
|
-
Q_Auth --> Q2["What happens if an unauthorized tenant accesses this resource ID?"]
|
|
36
|
-
Q2 --> Q_Sec["1. Return 404 Not Found (enumeration masking) or 403 Forbidden?\n2. Is isolation enforced at the DB layer (RLS / AST interceptor)?"]
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Decision Tree 3: External Integrations & 3rd-Party APIs
|
|
42
|
-
|
|
43
|
-
```mermaid
|
|
44
|
-
flowchart TD
|
|
45
|
-
Q1["Does the feature integrate with an external SaaS or network endpoint?"]
|
|
46
|
-
Q1 -->|Yes| Q2["What is the failure tolerance of the integration?"]
|
|
47
|
-
|
|
48
|
-
Q2 -->|Synchronous / Critical| Q_Sync["1. What is the strict HTTP timeout (e.g. 3000ms)?\n2. What is the circuit breaker threshold before fast-failing?\n3. What fallback response is served if the 3rd-party is down?"]
|
|
49
|
-
Q2 -->|Asynchronous / Event-Driven| Q_Async["1. Does the external system provide webhooks?\n2. How are webhook signatures cryptographically verified?\n3. What is the retry backoff and dead-letter queue (DLQ) policy?"]
|
|
50
|
-
|
|
51
|
-
Q_Sync --> Q_Port["How is the external SDK isolated?\n(Enforce application-owned Port interface so domain never imports SDK)"]
|
|
52
|
-
Q_Async --> Q_Port
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Decision Tree 4: Read Performance, Caching & Search
|
|
58
|
-
|
|
59
|
-
```mermaid
|
|
60
|
-
flowchart TD
|
|
61
|
-
Q1["What is the expected read volume and latency requirement?"]
|
|
62
|
-
Q1 -->|High Volume / Sub-50ms Latency| Q2["Is stale data acceptable for seconds/minutes?"]
|
|
63
|
-
|
|
64
|
-
Q2 -->|Yes| Q_Cache["1. What is the cache TTL and jitter window?\n2. What domain events trigger cache eviction?\n3. Is probabilistic early expiration (XFetch) needed?"]
|
|
65
|
-
Q2 -->|No: Strict Read-After-Write Consistency| Q_Consistent["1. Read from primary database instance for 2s after mutation\n2. Bypass read replicas during write session"]
|
|
66
|
-
|
|
67
|
-
Q_Cache --> Q_Page["Pagination Strategy: Enforce keyset/cursor pagination over OFFSET"]
|
|
68
|
-
Q_Consistent --> Q_Page
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## Decision Tree 5: User Interface, Experience Duality & Interaction Flows
|
|
74
|
-
|
|
75
|
-
```mermaid
|
|
76
|
-
flowchart TD
|
|
77
|
-
Q1["Does the feature introduce or modify a user interface?"]
|
|
78
|
-
Q1 -->|Yes| Q2["Who is the primary actor and operational persona?"]
|
|
79
|
-
|
|
80
|
-
Q2 -->|Operator / Admin| Q_Op["1. Information Density: Dense tabular grid with filters?\n2. Persistent App Shell: Left collapsible sidebar route?\n3. Actions: Inline row actions or full-page drawer?"]
|
|
81
|
-
Q2 -->|Consumer / Member| Q_Member["1. Experience Duality: Consumer portal (/portal)?\n2. Touch Ergonomics: Clean cards & mobile drawer?\n3. Simplified self-service actions?"]
|
|
82
|
-
|
|
83
|
-
Q_Op --> Q3["Navigation & State Synchronization"]
|
|
84
|
-
Q_Member --> Q3
|
|
85
|
-
|
|
86
|
-
Q3 --> Q_State["1. URL State: Deep-link query params (?tab=, ?q=, ?page=, ?modal=)?\n2. Server Cache: TanStack Query hook with automated invalidation?\n3. Accessibility: Accessible headless dialogs & ARIA live regions?"]
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Contextual Follow-Up Patterns
|
|
92
|
-
|
|
93
|
-
When conducting the interview, use this exact syntax pattern to chain questions adaptively:
|
|
94
|
-
|
|
95
|
-
1. **Acknowledge and Pin Previous Answer**:
|
|
96
|
-
`"Understood, you specified [Option A] for [Requirement X]."`
|
|
97
|
-
2. **Surface Immediate Architectural Implication**:
|
|
98
|
-
`"Because of [Option A], [Potential Failure / Edge Case Y] becomes the primary risk."`
|
|
99
|
-
3. **Ask Context-Dependent Question**:
|
|
100
|
-
`"How should the system behave when [Condition Y] occurs? Specifically:"`
|
|
101
|
-
- *Sub-question 1*
|
|
102
|
-
- *Sub-question 2*
|
|
1
|
+
# Adaptive Questioning Trees & Contextual Branching Matrices
|
|
2
|
+
|
|
3
|
+
> **Core Purpose:** Detailed decision trees for the `relentless-questioner` skill, demonstrating how subsequent questions adapt dynamically based on previous user responses.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Decision Tree 1: Mutating Operations & State Changes
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart TD
|
|
11
|
+
Q1["Does the operation mutate database state?"]
|
|
12
|
+
Q1 -->|Yes| Q2["Does it involve multiple tables, monetary balances, or inventory?"]
|
|
13
|
+
Q1 -->|No / Read Only| Q_Read["Branch: Read Performance & Consistency"]
|
|
14
|
+
|
|
15
|
+
Q2 -->|Yes: Financial / Inventory| Q_Acid["1. Transaction Isolation: REPEATABLE READ or SERIALIZABLE?\n2. Lock Ordering: How to prevent deadlocks?\n3. Concurrency: Optimistic Concurrency Control (version) or pessimistic locking?"]
|
|
16
|
+
Q2 -->|No: Standard Entity CRUD| Q_Crud["1. Soft delete or hard delete?\n2. Unique constraints across tenant?\n3. Cascading relations?"]
|
|
17
|
+
|
|
18
|
+
Q_Acid --> Q3["Does the mutation emit domain events or notify external systems?"]
|
|
19
|
+
Q_Crud --> Q3
|
|
20
|
+
|
|
21
|
+
Q3 -->|Yes| Q_Outbox["How is the dual-write avoided?\n(Enforce Transactional Outbox pattern before broker publish)"]
|
|
22
|
+
Q3 -->|No| Q4["Idempotency: Is an Idempotency-Key header required to guard against network retries?"]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Decision Tree 2: Multi-Tenancy & Authorization Boundaries
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart TD
|
|
31
|
+
Q1["Who executes this action and across which boundary?"]
|
|
32
|
+
Q1 -->|End User via Web/API| Q_Auth["1. What roles are permitted (ADMIN, MEMBER, CUSTOMER)?\n2. Are dynamic ABAC attributes involved (e.g. order value threshold)?\n3. Can a user act across multiple tenants (switch tenant)?"]
|
|
33
|
+
Q1 -->|System / Background Job| Q_Worker["1. How is tenant context established without an HTTP session?\n2. What service principal / token credentials are used?"]
|
|
34
|
+
|
|
35
|
+
Q_Auth --> Q2["What happens if an unauthorized tenant accesses this resource ID?"]
|
|
36
|
+
Q2 --> Q_Sec["1. Return 404 Not Found (enumeration masking) or 403 Forbidden?\n2. Is isolation enforced at the DB layer (RLS / AST interceptor)?"]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Decision Tree 3: External Integrations & 3rd-Party APIs
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart TD
|
|
45
|
+
Q1["Does the feature integrate with an external SaaS or network endpoint?"]
|
|
46
|
+
Q1 -->|Yes| Q2["What is the failure tolerance of the integration?"]
|
|
47
|
+
|
|
48
|
+
Q2 -->|Synchronous / Critical| Q_Sync["1. What is the strict HTTP timeout (e.g. 3000ms)?\n2. What is the circuit breaker threshold before fast-failing?\n3. What fallback response is served if the 3rd-party is down?"]
|
|
49
|
+
Q2 -->|Asynchronous / Event-Driven| Q_Async["1. Does the external system provide webhooks?\n2. How are webhook signatures cryptographically verified?\n3. What is the retry backoff and dead-letter queue (DLQ) policy?"]
|
|
50
|
+
|
|
51
|
+
Q_Sync --> Q_Port["How is the external SDK isolated?\n(Enforce application-owned Port interface so domain never imports SDK)"]
|
|
52
|
+
Q_Async --> Q_Port
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Decision Tree 4: Read Performance, Caching & Search
|
|
58
|
+
|
|
59
|
+
```mermaid
|
|
60
|
+
flowchart TD
|
|
61
|
+
Q1["What is the expected read volume and latency requirement?"]
|
|
62
|
+
Q1 -->|High Volume / Sub-50ms Latency| Q2["Is stale data acceptable for seconds/minutes?"]
|
|
63
|
+
|
|
64
|
+
Q2 -->|Yes| Q_Cache["1. What is the cache TTL and jitter window?\n2. What domain events trigger cache eviction?\n3. Is probabilistic early expiration (XFetch) needed?"]
|
|
65
|
+
Q2 -->|No: Strict Read-After-Write Consistency| Q_Consistent["1. Read from primary database instance for 2s after mutation\n2. Bypass read replicas during write session"]
|
|
66
|
+
|
|
67
|
+
Q_Cache --> Q_Page["Pagination Strategy: Enforce keyset/cursor pagination over OFFSET"]
|
|
68
|
+
Q_Consistent --> Q_Page
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Decision Tree 5: User Interface, Experience Duality & Interaction Flows
|
|
74
|
+
|
|
75
|
+
```mermaid
|
|
76
|
+
flowchart TD
|
|
77
|
+
Q1["Does the feature introduce or modify a user interface?"]
|
|
78
|
+
Q1 -->|Yes| Q2["Who is the primary actor and operational persona?"]
|
|
79
|
+
|
|
80
|
+
Q2 -->|Operator / Admin| Q_Op["1. Information Density: Dense tabular grid with filters?\n2. Persistent App Shell: Left collapsible sidebar route?\n3. Actions: Inline row actions or full-page drawer?"]
|
|
81
|
+
Q2 -->|Consumer / Member| Q_Member["1. Experience Duality: Consumer portal (/portal)?\n2. Touch Ergonomics: Clean cards & mobile drawer?\n3. Simplified self-service actions?"]
|
|
82
|
+
|
|
83
|
+
Q_Op --> Q3["Navigation & State Synchronization"]
|
|
84
|
+
Q_Member --> Q3
|
|
85
|
+
|
|
86
|
+
Q3 --> Q_State["1. URL State: Deep-link query params (?tab=, ?q=, ?page=, ?modal=)?\n2. Server Cache: TanStack Query hook with automated invalidation?\n3. Accessibility: Accessible headless dialogs & ARIA live regions?"]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Contextual Follow-Up Patterns
|
|
92
|
+
|
|
93
|
+
When conducting the interview, use this exact syntax pattern to chain questions adaptively:
|
|
94
|
+
|
|
95
|
+
1. **Acknowledge and Pin Previous Answer**:
|
|
96
|
+
`"Understood, you specified [Option A] for [Requirement X]."`
|
|
97
|
+
2. **Surface Immediate Architectural Implication**:
|
|
98
|
+
`"Because of [Option A], [Potential Failure / Edge Case Y] becomes the primary risk."`
|
|
99
|
+
3. **Ask Context-Dependent Question**:
|
|
100
|
+
`"How should the system behave when [Condition Y] occurs? Specifically:"`
|
|
101
|
+
- *Sub-question 1*
|
|
102
|
+
- *Sub-question 2*
|
package/.editorconfig
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# http://editorconfig.org
|
|
2
|
-
root = true
|
|
3
|
-
|
|
4
|
-
[*]
|
|
5
|
-
indent_style = space
|
|
6
|
-
indent_size = 2
|
|
7
|
-
end_of_line = lf
|
|
8
|
-
charset = utf-8
|
|
9
|
-
trim_trailing_whitespace = true
|
|
10
|
-
insert_final_newline = true
|
|
11
|
-
|
|
12
|
-
[*.md]
|
|
13
|
-
trim_trailing_whitespace = false
|
|
14
|
-
|
|
15
|
-
[Makefile]
|
|
16
|
-
indent_style = tab
|
|
17
|
-
|
|
18
|
-
[*.go]
|
|
19
|
-
indent_style = tab
|
|
1
|
+
# http://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
indent_style = space
|
|
6
|
+
indent_size = 2
|
|
7
|
+
end_of_line = lf
|
|
8
|
+
charset = utf-8
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
insert_final_newline = true
|
|
11
|
+
|
|
12
|
+
[*.md]
|
|
13
|
+
trim_trailing_whitespace = false
|
|
14
|
+
|
|
15
|
+
[Makefile]
|
|
16
|
+
indent_style = tab
|
|
17
|
+
|
|
18
|
+
[*.go]
|
|
19
|
+
indent_style = tab
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -1,56 +1,167 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main ]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- name:
|
|
40
|
-
uses:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- name:
|
|
53
|
-
run:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
merge_group:
|
|
9
|
+
branches: [ main ]
|
|
10
|
+
|
|
11
|
+
# Least privilege by default. Every job here only needs to read the repository.
|
|
12
|
+
# A write-capable token executing `node --test` over PR-controlled content is a
|
|
13
|
+
# supply-chain exposure; grant write scopes only where a job genuinely needs them.
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
# Cancel superseded runs. Without this, five pushes to a PR queue five full
|
|
18
|
+
# matrix runs; with a publish workflow it also prevents two release runs racing
|
|
19
|
+
# the same version.
|
|
20
|
+
concurrency:
|
|
21
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
22
|
+
cancel-in-progress: true
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
test:
|
|
26
|
+
name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
timeout-minutes: 15
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
os: [ ubuntu-24.04, macos-latest, windows-latest ]
|
|
33
|
+
# Node 18 (EOL 2025-04-30) and 20 (EOL 2026-04-30) are no longer
|
|
34
|
+
# patched. Half the matrix was verifying unpatched runtimes. Supported
|
|
35
|
+
# lines as of Oct 2026: 24 (Active LTS), 22 (Maintenance), 26 (Current).
|
|
36
|
+
node-version: [ 22.x, 24.x, 26.x ]
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout Repository
|
|
40
|
+
uses: actions/checkout@v7
|
|
41
|
+
|
|
42
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
43
|
+
uses: actions/setup-node@v7
|
|
44
|
+
with:
|
|
45
|
+
node-version: ${{ matrix.node-version }}
|
|
46
|
+
|
|
47
|
+
# The repo has zero runtime dependencies, but contributor tooling
|
|
48
|
+
# (ESLint, ADR-012) is a devDependency and must be installed.
|
|
49
|
+
- name: Install dependencies
|
|
50
|
+
run: npm ci
|
|
51
|
+
|
|
52
|
+
- name: Verify Environment
|
|
53
|
+
run: |
|
|
54
|
+
node --version
|
|
55
|
+
npm --version
|
|
56
|
+
|
|
57
|
+
- name: Run Syntax & Lint Checks
|
|
58
|
+
run: npm run lint
|
|
59
|
+
|
|
60
|
+
- name: Validate Agentic Architecture
|
|
61
|
+
run: npm run validate
|
|
62
|
+
|
|
63
|
+
- name: Run Test Suite
|
|
64
|
+
run: npm test
|
|
65
|
+
|
|
66
|
+
coverage:
|
|
67
|
+
name: Coverage Gate (Node 24)
|
|
68
|
+
runs-on: ubuntu-24.04
|
|
69
|
+
timeout-minutes: 15
|
|
70
|
+
steps:
|
|
71
|
+
- name: Checkout Repository
|
|
72
|
+
uses: actions/checkout@v7
|
|
73
|
+
|
|
74
|
+
- name: Setup Node.js 24.x
|
|
75
|
+
uses: actions/setup-node@v7
|
|
76
|
+
with:
|
|
77
|
+
node-version: 24.x
|
|
78
|
+
|
|
79
|
+
- name: Install dependencies
|
|
80
|
+
run: npm ci
|
|
81
|
+
|
|
82
|
+
# Native threshold flags require Node >=22.8.0 (see scripts/test_coverage.js),
|
|
83
|
+
# so the coverage gate runs once on the newest runtime instead of every matrix cell.
|
|
84
|
+
- name: Verify Coverage Gates
|
|
85
|
+
run: npm run test:coverage
|
|
86
|
+
|
|
87
|
+
shell-syntax:
|
|
88
|
+
name: Shell Script Syntax (shipped hooks)
|
|
89
|
+
runs-on: ubuntu-24.04
|
|
90
|
+
timeout-minutes: 10
|
|
91
|
+
steps:
|
|
92
|
+
- name: Checkout Repository
|
|
93
|
+
uses: actions/checkout@v7
|
|
94
|
+
|
|
95
|
+
# The .sh files ship to every scaffolded project and auto-run as agent
|
|
96
|
+
# hooks. `bash -n` catches a CRLF checkout or a syntax slip that the
|
|
97
|
+
# Node-only lint is blind to.
|
|
98
|
+
- name: Parse every shipped shell script
|
|
99
|
+
run: |
|
|
100
|
+
set -euo pipefail
|
|
101
|
+
failed=0
|
|
102
|
+
while IFS= read -r script; do
|
|
103
|
+
if bash -n "$script"; then
|
|
104
|
+
echo "ok: $script"
|
|
105
|
+
else
|
|
106
|
+
echo "FAIL: $script"
|
|
107
|
+
failed=1
|
|
108
|
+
fi
|
|
109
|
+
done < <(find .agents -name '*.sh' -not -path './node_modules/*')
|
|
110
|
+
exit $failed
|
|
111
|
+
|
|
112
|
+
- name: Verify hooks use LF line endings
|
|
113
|
+
run: |
|
|
114
|
+
set -euo pipefail
|
|
115
|
+
bad=0
|
|
116
|
+
while IFS= read -r script; do
|
|
117
|
+
if grep -qU $'\r' "$script"; then
|
|
118
|
+
echo "CRLF found (breaks shebang + 'set -euo pipefail'): $script"
|
|
119
|
+
bad=1
|
|
120
|
+
fi
|
|
121
|
+
done < <(find .agents -name '*.sh' -not -path './node_modules/*')
|
|
122
|
+
[ "$bad" -eq 0 ] && echo "all hook scripts are LF"
|
|
123
|
+
|
|
124
|
+
security:
|
|
125
|
+
name: Secret & Vulnerability Scans
|
|
126
|
+
runs-on: ubuntu-24.04
|
|
127
|
+
timeout-minutes: 20
|
|
128
|
+
permissions:
|
|
129
|
+
contents: read
|
|
130
|
+
# gitleaks-action comments on PRs when it finds a leak; without these it
|
|
131
|
+
# degrades gracefully to scan-only.
|
|
132
|
+
pull-requests: write
|
|
133
|
+
steps:
|
|
134
|
+
- name: Checkout Repository (full history for secret scanning)
|
|
135
|
+
uses: actions/checkout@v7
|
|
136
|
+
with:
|
|
137
|
+
fetch-depth: 0
|
|
138
|
+
|
|
139
|
+
# gitleaks-action v2 runs on the Node 20 action runtime, which GitHub
|
|
140
|
+
# removed from hosted runners on 2026-09-16 -- the job was failing for
|
|
141
|
+
# every PR. v3 targets Node 24.
|
|
142
|
+
- name: Secret Scan (Gitleaks)
|
|
143
|
+
uses: gitleaks/gitleaks-action@v3
|
|
144
|
+
env:
|
|
145
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
146
|
+
|
|
147
|
+
- name: Filesystem Vulnerability Scan (Trivy)
|
|
148
|
+
uses: aquasecurity/trivy-action@v0.36.0
|
|
149
|
+
with:
|
|
150
|
+
scan-type: fs
|
|
151
|
+
scan-ref: .
|
|
152
|
+
scanners: vuln
|
|
153
|
+
severity: HIGH,CRITICAL
|
|
154
|
+
exit-code: '1'
|
|
155
|
+
|
|
156
|
+
- name: Software Bill of Materials (CycloneDX)
|
|
157
|
+
uses: anchore/sbom-action@v0
|
|
158
|
+
with:
|
|
159
|
+
format: cyclonedx-json
|
|
160
|
+
output-file: sbom.cdx.json
|
|
161
|
+
artifact-name: sbom.cdx.json
|
|
162
|
+
|
|
163
|
+
- name: Dependency Review
|
|
164
|
+
if: github.event_name == 'pull_request'
|
|
165
|
+
uses: actions/dependency-review-action@v4
|
|
166
|
+
with:
|
|
167
|
+
fail-on-severity: high
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [ published ]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
dry-run:
|
|
9
|
+
description: "Verify the publishable tarball without uploading"
|
|
10
|
+
type: boolean
|
|
11
|
+
default: true
|
|
12
|
+
|
|
13
|
+
# Least privilege: read the repo, write only the npm registry.
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
id-token: write # npm trusted publishing / provenance attestation
|
|
17
|
+
|
|
18
|
+
# Never let two release runs race the same version.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: publish-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
verify:
|
|
25
|
+
name: Verify Release Before Publishing
|
|
26
|
+
runs-on: ubuntu-24.04
|
|
27
|
+
timeout-minutes: 15
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout Repository
|
|
30
|
+
uses: actions/checkout@v7
|
|
31
|
+
|
|
32
|
+
- name: Setup Node.js 24.x
|
|
33
|
+
uses: actions/setup-node@v7
|
|
34
|
+
with:
|
|
35
|
+
node-version: 24.x
|
|
36
|
+
registry-url: https://registry.npmjs.org
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: npm ci
|
|
40
|
+
|
|
41
|
+
- name: Verify Environment
|
|
42
|
+
run: |
|
|
43
|
+
node --version
|
|
44
|
+
npm --version
|
|
45
|
+
|
|
46
|
+
# Re-run the full gate on the exact release tag. prepublishOnly also runs
|
|
47
|
+
# on `npm publish`, but failing fast here avoids building a tarball at all.
|
|
48
|
+
- name: Run Syntax & Lint Checks
|
|
49
|
+
run: npm run lint
|
|
50
|
+
|
|
51
|
+
- name: Run Test Suite
|
|
52
|
+
run: npm test
|
|
53
|
+
|
|
54
|
+
- name: Verify Coverage Gates
|
|
55
|
+
run: npm run test:coverage
|
|
56
|
+
|
|
57
|
+
- name: Validate Agentic Architecture
|
|
58
|
+
run: npm run validate
|
|
59
|
+
|
|
60
|
+
- name: Verify version matches the release tag
|
|
61
|
+
if: github.event_name == 'release'
|
|
62
|
+
run: |
|
|
63
|
+
set -euo pipefail
|
|
64
|
+
pkg_version="$(node -p "require('./package.json').version")"
|
|
65
|
+
tag_version="${GITHUB_REF_NAME#v}"
|
|
66
|
+
if [ "$pkg_version" != "$tag_version" ]; then
|
|
67
|
+
echo "::error::package.json version ($pkg_version) does not match release tag ($GITHUB_REF_NAME)"
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
echo "version $pkg_version matches tag $GITHUB_REF_NAME"
|
|
71
|
+
|
|
72
|
+
- name: Inspect the publishable tarball
|
|
73
|
+
run: |
|
|
74
|
+
set -euo pipefail
|
|
75
|
+
npm pack --dry-run
|
|
76
|
+
# Prove the tarball actually carries the files the package promises,
|
|
77
|
+
# rather than trusting the files[] allowlist to be correct.
|
|
78
|
+
# npm writes UTF-16LE on Windows, so parse it as text, not via require.
|
|
79
|
+
npm pack --json > pack.json
|
|
80
|
+
node -e '
|
|
81
|
+
const fs = require("fs");
|
|
82
|
+
let raw = fs.readFileSync("pack.json", "utf-8");
|
|
83
|
+
if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1);
|
|
84
|
+
// npm writes UTF-16LE on Windows, so utf-8 decoding yields NUL
|
|
85
|
+
// padding. Detect it and re-read. Use an escape, never a literal
|
|
86
|
+
// NUL byte: an embedded 0x00 makes git classify this file as
|
|
87
|
+
// binary, which breaks the workflow.
|
|
88
|
+
if (raw.indexOf(String.fromCharCode(0)) !== -1) {
|
|
89
|
+
raw = fs.readFileSync("pack.json", "utf16le");
|
|
90
|
+
if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1);
|
|
91
|
+
}
|
|
92
|
+
const meta = JSON.parse(raw)[0];
|
|
93
|
+
const files = new Set(meta.files.map((f) => f.path));
|
|
94
|
+
const required = [
|
|
95
|
+
"package.json", "README.md", "LICENSE",
|
|
96
|
+
"bin/azcodr.js", "lib/index.js", "lib/index.d.ts", "lib/scaffold.js",
|
|
97
|
+
"scripts/validate.js", "scripts/validate-cli.js"
|
|
98
|
+
];
|
|
99
|
+
const missing = required.filter((f) => !files.has(f));
|
|
100
|
+
if (missing.length) {
|
|
101
|
+
console.error("::error::missing from tarball: " + missing.join(", "));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
console.log("tarball contains all " + required.length + " required paths");
|
|
105
|
+
'
|
|
106
|
+
|
|
107
|
+
- name: Install the packed tarball and smoke-test the CLI
|
|
108
|
+
run: |
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
# The highest-value missing check: the published artifact must be
|
|
111
|
+
# runnable, not just constructible.
|
|
112
|
+
npm pack > /dev/null
|
|
113
|
+
mkdir -p /tmp/smoke && cd /tmp/smoke
|
|
114
|
+
npm init -y > /dev/null 2>&1
|
|
115
|
+
npm install "$GITHUB_WORKSPACE"/*.tgz --no-audit --no-fund
|
|
116
|
+
npx azcodr --version
|
|
117
|
+
npx azcodr --help > /dev/null
|
|
118
|
+
node -e '
|
|
119
|
+
const api = require("azcodr");
|
|
120
|
+
const required = ["scaffold", "copyTemplate", "validateTarget", "ERROR_CODES", "ScaffoldError"];
|
|
121
|
+
const missing = required.filter((k) => !(k in api));
|
|
122
|
+
if (missing.length) { console.error("missing exports: " + missing.join(", ")); process.exit(1); }
|
|
123
|
+
if (api.ERROR_CODES.E_GIT_BLOCKED !== "Blocked git subcommand") {
|
|
124
|
+
console.error("error-code contract changed unexpectedly");
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
console.log("programmatic API surface verified");
|
|
128
|
+
'
|
|
129
|
+
|
|
130
|
+
- name: Scaffold a real project from the packed tarball
|
|
131
|
+
run: |
|
|
132
|
+
set -euo pipefail
|
|
133
|
+
cd /tmp/smoke
|
|
134
|
+
npx azcodr ./generated --force --no-git
|
|
135
|
+
cd ./generated
|
|
136
|
+
npm run validate
|
|
137
|
+
|
|
138
|
+
publish-npm:
|
|
139
|
+
name: Publish to npm
|
|
140
|
+
needs: verify
|
|
141
|
+
runs-on: ubuntu-24.04
|
|
142
|
+
timeout-minutes: 15
|
|
143
|
+
steps:
|
|
144
|
+
- name: Checkout Repository
|
|
145
|
+
uses: actions/checkout@v7
|
|
146
|
+
|
|
147
|
+
- name: Setup Node.js 24.x
|
|
148
|
+
uses: actions/setup-node@v7
|
|
149
|
+
with:
|
|
150
|
+
node-version: 24.x
|
|
151
|
+
registry-url: https://registry.npmjs.org
|
|
152
|
+
|
|
153
|
+
- name: Verify the working tree is clean
|
|
154
|
+
run: |
|
|
155
|
+
set -euo pipefail
|
|
156
|
+
# Publishing from a dirty tree ships files that are not in any tag and
|
|
157
|
+
# cannot be reproduced later.
|
|
158
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
159
|
+
echo "::error::working tree is dirty; refusing to publish"
|
|
160
|
+
git status --porcelain
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
- name: Install dependencies
|
|
165
|
+
run: npm ci
|
|
166
|
+
|
|
167
|
+
- name: Re-run the publish gate
|
|
168
|
+
# prepublishOnly fires on npm publish: lint -> coverage -> validate.
|
|
169
|
+
run: npm run lint && npm run test:coverage && npm run validate
|
|
170
|
+
|
|
171
|
+
- name: Dry run (no upload)
|
|
172
|
+
if: inputs.dry-run == true
|
|
173
|
+
run: npm publish --dry-run --access public
|
|
174
|
+
|
|
175
|
+
- name: Publish to npm
|
|
176
|
+
if: inputs.dry-run != true
|
|
177
|
+
run: npm publish --access public --provenance
|
|
178
|
+
env:
|
|
179
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
180
|
+
|
|
181
|
+
publish-jsr:
|
|
182
|
+
name: Publish to JSR
|
|
183
|
+
needs: verify
|
|
184
|
+
# Requires a native ESM entrypoint. The npm package is CommonJS, which JSR
|
|
185
|
+
# explicitly forbids ("You cannot publish CommonJS modules"). Disabled with
|
|
186
|
+
# a stated reason rather than failing every release.
|
|
187
|
+
if: false
|
|
188
|
+
runs-on: ubuntu-24.04
|
|
189
|
+
timeout-minutes: 15
|
|
190
|
+
steps:
|
|
191
|
+
- name: Checkout Repository
|
|
192
|
+
uses: actions/checkout@v7
|
|
193
|
+
|
|
194
|
+
- name: Setup Deno
|
|
195
|
+
uses: denoland/setup-deno@v2
|
|
196
|
+
with:
|
|
197
|
+
deno-version: v2.x
|
|
198
|
+
|
|
199
|
+
- name: Publish to JSR
|
|
200
|
+
run: npx jsr publish
|