260318:1237 Fix UUID #4
Build and Deploy / deploy (push) Successful in 11m17s

This commit is contained in:
admin
2026-03-18 12:37:29 +07:00
parent 5d89079c2a
commit ba642e7e42
71 changed files with 533 additions and 319 deletions
+78 -78
View File
@@ -15,7 +15,7 @@ Welcome to the **Antigravity Edition** of Spec-Kit. This system is architected t
In this edition, Spec-Kit commands have been split into two interactive layers:
1. **Workflows (`/command`)**: High-level orchestrations that guide the agent through a series of logical steps. **The easiest way to run a skill is by typing its corresponding workflow command.**
2. **Skills (`@speckit.name`)**: Packaged agentic capabilities. Mentions of a skill give the agent immediate context and autonomous "know-how" to execute the specific toolset associated with that phase.
2. **Skills (`@speckit-name`)**: Packaged agentic capabilities. Mentions of a skill give the agent immediate context and autonomous "know-how" to execute the specific toolset associated with that phase.
> **To understand the power of Skills in Antigravity, read the docs here:**
> [https://antigravity.google/docs/skills](https://antigravity.google/docs/skills)
@@ -38,15 +38,15 @@ Some skills and scripts reference a `.specify/` directory for templates and proj
```text
.specify/
├── templates/
│ ├── spec-template.md # Template for /speckit.specify
│ ├── plan-template.md # Template for /speckit.plan
│ ├── tasks-template.md # Template for /speckit.tasks
│ ├── spec-template.md # Template for /speckit-specify
│ ├── plan-template.md # Template for /speckit-plan
│ ├── tasks-template.md # Template for /speckit-tasks
│ └── agent-file-template.md # Template for update-agent-context.sh
└── memory/
└── constitution.md # Project governance rules (/speckit.constitution)
└── constitution.md # Project governance rules (/speckit-constitution)
```
> **Note:** If `.specify/` is absent, skills will still function — they'll create blank files instead of using templates. The constitution workflow (`/speckit.constitution`) will create this structure for you on first run.
> **Note:** If `.specify/` is absent, skills will still function — they'll create blank files instead of using templates. The constitution workflow (`/speckit-constitution`) will create this structure for you on first run.
---
@@ -59,34 +59,34 @@ The toolkit is organized into modular components that provide both the logic (Sc
├── skills/ # @ Mentions (Agent Intelligence)
│ ├── nestjs-best-practices/ # NestJS Architecture Patterns
│ ├── next-best-practices/ # Next.js App Router Patterns
│ ├── speckit.analyze/ # Consistency Checker
│ ├── speckit.checker/ # Static Analysis Aggregator
│ ├── speckit.checklist/ # Requirements Validator
│ ├── speckit.clarify/ # Ambiguity Resolver
│ ├── speckit.constitution/ # Governance Manager
│ ├── speckit.diff/ # Artifact Comparator
│ ├── speckit.implement/ # Code Builder (Anti-Regression)
│ ├── speckit.migrate/ # Legacy Code Migrator
│ ├── speckit.plan/ # Technical Planner
│ ├── speckit.quizme/ # Logic Challenger (Red Team)
│ ├── speckit.reviewer/ # Code Reviewer
│ ├── speckit.security-audit/ # Security Auditor (OWASP/CASL/ClamAV)
│ ├── speckit.specify/ # Feature Definer
│ ├── speckit.status/ # Progress Dashboard
│ ├── speckit.tasks/ # Task Breaker
│ ├── speckit.taskstoissues/ # Issue Tracker Syncer (GitHub + Gitea)
│ ├── speckit.tester/ # Test Runner & Coverage
│ └── speckit.validate/ # Implementation Validator
│ ├── speckit-analyze/ # Consistency Checker
│ ├── speckit-checker/ # Static Analysis Aggregator
│ ├── speckit-checklist/ # Requirements Validator
│ ├── speckit-clarify/ # Ambiguity Resolver
│ ├── speckit-constitution/ # Governance Manager
│ ├── speckit-diff/ # Artifact Comparator
│ ├── speckit-implement/ # Code Builder (Anti-Regression)
│ ├── speckit-migrate/ # Legacy Code Migrator
│ ├── speckit-plan/ # Technical Planner
│ ├── speckit-quizme/ # Logic Challenger (Red Team)
│ ├── speckit-reviewer/ # Code Reviewer
│ ├── speckit-security-audit/ # Security Auditor (OWASP/CASL/ClamAV)
│ ├── speckit-specify/ # Feature Definer
│ ├── speckit-status/ # Progress Dashboard
│ ├── speckit-tasks/ # Task Breaker
│ ├── speckit-taskstoissues/ # Issue Tracker Syncer (GitHub + Gitea)
│ ├── speckit-tester/ # Test Runner & Coverage
│ └── speckit-validate/ # Implementation Validator
├── workflows/ # / Slash Commands (Orchestration)
│ ├── 00-speckit.all.md # Full Pipeline (10 steps: Specify → Validate)
│ ├── 0111-speckit.*.md # Individual phase workflows
│ ├── speckit.prepare.md # Prep Pipeline (5 steps: Specify → Analyze)
│ ├── 00-speckit-all.md # Full Pipeline (10 steps: Specify → Validate)
│ ├── 0111-speckit-*.md # Individual phase workflows
│ ├── speckit-prepare.md # Prep Pipeline (5 steps: Specify → Analyze)
│ ├── schema-change.md # DB Schema Change (ADR-009)
│ ├── create-backend-module.md # NestJS Module Scaffolding
│ ├── create-frontend-page.md # Next.js Page Scaffolding
│ ├── deploy.md # Deployment via Gitea CI/CD
│ └── util-speckit.*.md # Utilities (checklist, diff, migrate, etc.)
│ └── util-speckit-*.md # Utilities (checklist, diff, migrate, etc.)
└── scripts/
├── bash/ # Bash Core (Kinetic logic)
@@ -112,27 +112,27 @@ The toolkit is organized into modular components that provide both the logic (Sc
| Phase | Workflow Trigger | Antigravity Skill | Role |
| :---------------- | :---------------------------- | :------------------------ | :------------------------------------------------------ |
| **Full Pipeline** | `/00-speckit.all` | N/A | Runs full SDLC pipeline (10 steps: Specify → Validate). |
| **Governance** | `/01-speckit.constitution` | `@speckit.constitution` | Establishes project rules & principles. |
| **Definition** | `/02-speckit.specify` | `@speckit.specify` | Drafts structured `spec.md`. |
| **Ambiguity** | `/03-speckit.clarify` | `@speckit.clarify` | Resolves gaps post-spec. |
| **Architecture** | `/04-speckit.plan` | `@speckit.plan` | Generates technical `plan.md`. |
| **Decomposition** | `/05-speckit.tasks` | `@speckit.tasks` | Breaks plans into atomic tasks. |
| **Consistency** | `/06-speckit.analyze` | `@speckit.analyze` | Cross-checks Spec vs Plan vs Tasks. |
| **Execution** | `/07-speckit.implement` | `@speckit.implement` | Builds implementation with safety protocols. |
| **Quality** | `/08-speckit.checker` | `@speckit.checker` | Runs static analysis (Linting, Security, Types). |
| **Testing** | `/09-speckit.tester` | `@speckit.tester` | Runs test suite & reports coverage. |
| **Review** | `/10-speckit.reviewer` | `@speckit.reviewer` | Performs code review (Logic, Perf, Style). |
| **Validation** | `/11-speckit.validate` | `@speckit.validate` | Verifies implementation matches Spec requirements. |
| **Preparation** | `/speckit.prepare` | N/A | Runs Specify → Analyze prep sequence (5 steps). |
| **Full Pipeline** | `/00-speckit-all` | N/A | Runs full SDLC pipeline (10 steps: Specify → Validate). |
| **Governance** | `/01-speckit-constitution` | `@speckit-constitution` | Establishes project rules & principles. |
| **Definition** | `/02-speckit-specify` | `@speckit-specify` | Drafts structured `spec.md`. |
| **Ambiguity** | `/03-speckit-clarify` | `@speckit-clarify` | Resolves gaps post-spec. |
| **Architecture** | `/04-speckit-plan` | `@speckit-plan` | Generates technical `plan.md`. |
| **Decomposition** | `/05-speckit-tasks` | `@speckit-tasks` | Breaks plans into atomic tasks. |
| **Consistency** | `/06-speckit-analyze` | `@speckit-analyze` | Cross-checks Spec vs Plan vs Tasks. |
| **Execution** | `/07-speckit-implement` | `@speckit-implement` | Builds implementation with safety protocols. |
| **Quality** | `/08-speckit-checker` | `@speckit-checker` | Runs static analysis (Linting, Security, Types). |
| **Testing** | `/09-speckit-tester` | `@speckit-tester` | Runs test suite & reports coverage. |
| **Review** | `/10-speckit-reviewer` | `@speckit-reviewer` | Performs code review (Logic, Perf, Style). |
| **Validation** | `/11-speckit-validate` | `@speckit-validate` | Verifies implementation matches Spec requirements. |
| **Preparation** | `/speckit-prepare` | N/A | Runs Specify → Analyze prep sequence (5 steps). |
| **Schema** | `/schema-change` | N/A | DB schema changes per ADR-009 (no migrations). |
| **Security** | N/A | `@speckit.security-audit` | OWASP Top 10 + CASL + ClamAV audit. |
| **Checklist** | `/util-speckit.checklist` | `@speckit.checklist` | Generates feature checklists. |
| **Diff** | `/util-speckit.diff` | `@speckit.diff` | Compares artifact versions. |
| **Migration** | `/util-speckit.migrate` | `@speckit.migrate` | Port existing code to Spec-Kit. |
| **Red Team** | `/util-speckit.quizme` | `@speckit.quizme` | Challenges logical flaws. |
| **Status** | `/util-speckit.status` | `@speckit.status` | Shows feature completion status. |
| **Tracking** | `/util-speckit.taskstoissues` | `@speckit.taskstoissues` | Syncs tasks to GitHub/Gitea issues. |
| **Security** | N/A | `@speckit-security-audit` | OWASP Top 10 + CASL + ClamAV audit. |
| **Checklist** | `/util-speckit-checklist` | `@speckit-checklist` | Generates feature checklists. |
| **Diff** | `/util-speckit-diff` | `@speckit-diff` | Compares artifact versions. |
| **Migration** | `/util-speckit-migrate` | `@speckit-migrate` | Port existing code to Spec-Kit. |
| **Red Team** | `/util-speckit-quizme` | `@speckit-quizme` | Challenges logical flaws. |
| **Status** | `/util-speckit-status` | `@speckit-status` | Shows feature completion status. |
| **Tracking** | `/util-speckit-taskstoissues` | `@speckit-taskstoissues` | Syncs tasks to GitHub/Gitea issues. |
---
@@ -142,14 +142,14 @@ The following skills are designed to work together as a comprehensive defense ag
| Step | Skill | Core Question | Focus |
| :-------------- | :------------------ | :-------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. Checker** | `@speckit.checker` | _"Is the code compliant?"_ | **Syntax & Security**. Runs compilation, linting (ESLint/GolangCI), and vulnerability scans (npm audit/govulncheck). Catches low-level errors first. |
| **2. Tester** | `@speckit.tester` | _"Does it work?"_ | **Functionality**. Executes your test suite (Jest/Pytest/Go Test) to ensure logic performs as expected and tests pass. |
| **3. Reviewer** | `@speckit.reviewer` | _"Is the code written well?"_ | **Quality & Maintainability**. Analyzes code structure for complexity, performance bottlenecks, and best practices, acting as a senior peer reviewer. |
| **4. Validate** | `@speckit.validate` | _"Did we build the right thing?"_ | **Requirements**. Semantically compares the implementation against the defined `spec.md` and `plan.md` to ensure all feature requirements are met. |
| **1. Checker** | `@speckit-checker` | _"Is the code compliant?"_ | **Syntax & Security**. Runs compilation, linting (ESLint/GolangCI), and vulnerability scans (npm audit/govulncheck). Catches low-level errors first. |
| **2. Tester** | `@speckit-tester` | _"Does it work?"_ | **Functionality**. Executes your test suite (Jest/Pytest/Go Test) to ensure logic performs as expected and tests pass. |
| **3. Reviewer** | `@speckit-reviewer` | _"Is the code written well?"_ | **Quality & Maintainability**. Analyzes code structure for complexity, performance bottlenecks, and best practices, acting as a senior peer reviewer. |
| **4. Validate** | `@speckit-validate` | _"Did we build the right thing?"_ | **Requirements**. Semantically compares the implementation against the defined `spec.md` and `plan.md` to ensure all feature requirements are met. |
> **🤖 Power User Tip:** You can amplify this pipeline by creating a custom **MCP Server** or subagent that delegates heavy reasoning to a dedicated LLM.
>
> - **Use Case:** Bind the `@speckit.validate` and `@speckit.reviewer` steps to a large-context model.
> - **Use Case:** Bind the `@speckit-validate` and `@speckit-reviewer` steps to a large-context model.
> - **Benefit:** Large-context models (1M+ tokens) excel at analyzing the full project context against the Spec, finding subtle logical flaws that smaller models miss.
> - **How:** Create a wrapper script `scripts/gemini-reviewer.sh` that pipes the `tasks.md` and codebase to an LLM, then expose this as a tool.
@@ -161,28 +161,28 @@ These workflows function as the "Control Plane" of the project, managing everyth
| Step | Workflow | Core Question | Focus |
| :----------------- | :-------------------------------------------------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. Preparation** | `/speckit.prepare` | _"Are we ready?"_ | **The Macro-Workflow**. Runs Skills 0206 (Specify $\to$ Clarify $\to$ Plan $\to$ Tasks $\to$ Analyze) in one sequence to go from "Idea" to "Ready to Code". |
| **2. Migration** | `/util-speckit.migrate` | _"Can we import?"_ | **Onboarding**. Reverse-engineers existing code into `spec.md`, `plan.md`, and `tasks.md`. |
| **3. Red Team** | `/util-speckit.quizme` | _"What did we miss?"_ | **Hardening**. Socratic questioning to find logical gaps in your specification before you plan. |
| **4. Export** | `/util-speckit.taskstoissues` | _"Who does what?"_ | **Handoff**. Converts your `tasks.md` into GitHub or Gitea issues with labels and milestones. |
| **5. Status** | `/util-speckit.status` | _"Are we there yet?"_ | **Tracking**. Scans all artifacts to report feature completion percentage. |
| **6. Utilities** | `/util-speckit.diff` <br> `/util-speckit.checklist` | _"What changed?"_ | **Support**. View artifact diffs or generate quick acceptance checklists. |
| **1. Preparation** | `/speckit-prepare` | _"Are we ready?"_ | **The Macro-Workflow**. Runs Skills 0206 (Specify $\to$ Clarify $\to$ Plan $\to$ Tasks $\to$ Analyze) in one sequence to go from "Idea" to "Ready to Code". |
| **2. Migration** | `/util-speckit-migrate` | _"Can we import?"_ | **Onboarding**. Reverse-engineers existing code into `spec.md`, `plan.md`, and `tasks.md`. |
| **3. Red Team** | `/util-speckit-quizme` | _"What did we miss?"_ | **Hardening**. Socratic questioning to find logical gaps in your specification before you plan. |
| **4. Export** | `/util-speckit-taskstoissues` | _"Who does what?"_ | **Handoff**. Converts your `tasks.md` into GitHub or Gitea issues with labels and milestones. |
| **5. Status** | `/util-speckit-status` | _"Are we there yet?"_ | **Tracking**. Scans all artifacts to report feature completion percentage. |
| **6. Utilities** | `/util-speckit-diff` <br> `/util-speckit-checklist` | _"What changed?"_ | **Support**. View artifact diffs or generate quick acceptance checklists. |
### 🔄 The Design Sequence
**Stage 1: Inception**
- _Legacy Project?_ $\to$ Run **`/util-speckit.migrate`**.
- _New Feature?_ $\to$ Run **`/speckit.prepare`**.
- _Legacy Project?_ $\to$ Run **`/util-speckit-migrate`**.
- _New Feature?_ $\to$ Run **`/speckit-prepare`**.
**Stage 2: Hardening**
- Run **`/util-speckit.quizme`** to catch edge cases.
- Run **`/speckit.prepare`** again to regenerate the Plan based on restricted specs.
- Run **`/util-speckit-quizme`** to catch edge cases.
- Run **`/speckit-prepare`** again to regenerate the Plan based on restricted specs.
**Stage 3: Handoff**
- Run **`/util-speckit.taskstoissues`** to sync with your board.
- Run **`/util-speckit-taskstoissues`** to sync with your board.
---
@@ -192,17 +192,17 @@ Once the `.agents` folder is present, you can talk to your agent as a partner. Y
**Example Conversation:**
> **Developer**: "**/02-speckit.specify** Add a real-time notification system using WebSockets."
> **Developer**: "**/02-speckit-specify** Add a real-time notification system using WebSockets."
>
> **Antigravity**: "Understood. Executing the **@speckit.specify** skill. Creating feature branch `005-notifications` and drafting the initial specification..."
> **Antigravity**: "Understood. Executing the **@speckit-specify** skill. Creating feature branch `005-notifications` and drafting the initial specification..."
> _(Antigravity creates branch, drafts spec.md)_
>
> **Antigravity**: "Specification ready. I've identified some ambiguity in the reconnection strategy. Should I use **@speckit.clarify** to resolve this?"
> **Antigravity**: "Specification ready. I've identified some ambiguity in the reconnection strategy. Should I use **@speckit-clarify** to resolve this?"
>
> **Developer**: "Yes, use **@speckit.clarify**."
> **Developer**: "Yes, use **@speckit-clarify**."
> _(Antigravity executes the skill logic, interviewing the developer and updating the spec)_
>
> **Antigravity**: "The spec is now robust. I'm ready to trigger **/04-speckit.plan** and **/05-speckit.tasks** to prepare for implementation."
> **Antigravity**: "The spec is now robust. I'm ready to trigger **/04-speckit-plan** and **/05-speckit-tasks** to prepare for implementation."
---
@@ -212,7 +212,7 @@ To get the most out of this system, follow these **Spec-Driven Development (SDD)
### 1. The Constitution is King 👑
**Never skip `/01-speckit.constitution`.**
**Never skip `/01-speckit-constitution`.**
- This file is the "Context Window Anchor" for the AI.
- It prevents hallucinations about tech stack (e.g., "Don't use jQuery" or "Always use TypeScript strict mode").
@@ -222,9 +222,9 @@ To get the most out of this system, follow these **Spec-Driven Development (SDD)
Don't rush to code. The workflow exists to catch errors _cheaply_ before they become expensive bugs.
- **Ambiguity Layer**: `/03-speckit.clarify` catches misunderstandings.
- **Logic Layer**: `/util-speckit.quizme` catches edge cases.
- **Consistency Layer**: `/06-speckit.analyze` catches gaps between Spec and Plan.
- **Ambiguity Layer**: `/03-speckit-clarify` catches misunderstandings.
- **Logic Layer**: `/util-speckit-quizme` catches edge cases.
- **Consistency Layer**: `/06-speckit-analyze` catches gaps between Spec and Plan.
### 3. The 15-Minute Rule ⏱️
@@ -240,17 +240,17 @@ If you change your mind mid-project:
1. Don't just edit the code.
2. Edit the `spec.md` to reflect the new requirement.
3. Run `/util-speckit.diff` to see the drift.
3. Run `/util-speckit-diff` to see the drift.
4. This keeps your documentation alive and truthful.
---
## 🧩 Adaptation Notes
- **Skill-Based Autonomy**: Mentions like `@speckit.plan` trigger the agent's internalized understanding of how to perform that role.
- **Skill-Based Autonomy**: Mentions like `@speckit-plan` trigger the agent's internalized understanding of how to perform that role.
- **Shared Script Core**: Logic resides in `.agents/scripts/bash` (modular) with PowerShell equivalents in `scripts/powershell/` for Windows-native execution.
- **Agent-Native**: Designed to be invoked via Antigravity tool calls and reasoning rather than just terminal strings.
- **LCBP3-DMS Specific**: Includes project-specific skills (`nestjs-best-practices`, `next-best-practices`, `speckit.security-audit`) and workflows (`/schema-change`, `/create-backend-module`, `/deploy`).
- **LCBP3-DMS Specific**: Includes project-specific skills (`nestjs-best-practices`, `next-best-practices`, `speckit-security-audit`) and workflows (`/schema-change`, `/create-backend-module`, `/deploy`).
---
@@ -287,8 +287,8 @@ If you change your mind mid-project:
| Create Frontend Page | `/create-frontend-page` | Next.js App Router page |
| Schema Change | `/schema-change` | ADR-009: No migrations |
| Deploy | `/deploy` | Blue-Green via Gitea CI/CD |
| UAT Feature Check | `/11-speckit.validate` | vs `01-05-acceptance-criteria.md` |
| Security Audit | `@speckit.security-audit` | OWASP + CASL + ClamAV |
| UAT Feature Check | `/11-speckit-validate` | vs `01-05-acceptance-criteria.md` |
| Security Audit | `@speckit-security-audit` | OWASP + CASL + ClamAV |
### 🚫 Critical Forbidden Actions
@@ -1,9 +1,9 @@
---
name: speckit.analyze
name: speckit-analyze
description: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation.
version: 1.0.0
depends-on:
- speckit.tasks
- speckit-tasks
---
## User Input
@@ -21,13 +21,13 @@ You are the **Antigravity Consistency Analyst**. Your role is to identify incons
## Task
### Goal
Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/speckit.tasks` has successfully produced a complete `tasks.md`.
Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/speckit-tasks` has successfully produced a complete `tasks.md`.
## Operating Constraints
**STRICTLY READ-ONLY**: Do **not** modify any files. Output a structured analysis report. Offer an optional remediation plan (user must explicitly approve before any follow-up editing commands would be invoked manually).
**Constitution Authority**: The project constitution (`.specify/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/speckit.analyze`.
**Constitution Authority**: The project constitution (`.specify/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/speckit-analyze`.
### Steps
@@ -163,9 +163,9 @@ Output a Markdown report (no file writes) with the following structure:
At end of report, output a concise Next Actions block:
- If CRITICAL issues exist: Recommend resolving before `/speckit.implement`
- If CRITICAL issues exist: Recommend resolving before `/speckit-implement`
- If only LOW/MEDIUM: User may proceed, but provide improvement suggestions
- Provide explicit command suggestions: e.g., "Run /speckit.specify with refinement", "Run /speckit.plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'"
- Provide explicit command suggestions: e.g., "Run /speckit-specify with refinement", "Run /speckit-plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'"
### 8. Offer Remediation
@@ -1,5 +1,5 @@
---
name: speckit.checker
name: speckit-checker
description: Run static analysis tools and aggregate results.
version: 1.0.0
depends-on: []
@@ -1,5 +1,5 @@
---
name: speckit.checklist
name: speckit-checklist
description: Generate a custom checklist for the current feature based on user requirements.
version: 1.0.0
---
@@ -101,7 +101,7 @@ You are the **Antigravity Quality Gatekeeper**. Your role is to validate the qua
- Format: `[domain].md`
- If file exists, append to existing file
- Number items sequentially starting from CHK001
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists)
- Each `/speckit-checklist` run creates a NEW file (never overwrites existing checklists)
**CORE PRINCIPLE - Test the Requirements, Not the Implementation**:
Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for:
@@ -219,7 +219,7 @@ You are the **Antigravity Quality Gatekeeper**. Your role is to validate the qua
- Actor/timing
- Any explicit user-specified must-have items incorporated
**Important**: Each `/speckit.checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows:
**Important**: Each `/speckit-checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows:
- Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`)
- Simple, memorable filenames that indicate checklist purpose
@@ -4,13 +4,13 @@
**Created**: [DATE]
**Feature**: [Link to spec.md or relevant documentation]
**Note**: This checklist is generated by the `/speckit.checklist` command based on feature context and requirements.
**Note**: This checklist is generated by the `/speckit-checklist` command based on feature context and requirements.
<!--
============================================================================
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
The /speckit.checklist command MUST replace these with actual items based on:
The /speckit-checklist command MUST replace these with actual items based on:
- User's specific checklist request
- Feature requirements from spec.md
- Technical context from plan.md
@@ -1,12 +1,12 @@
---
name: speckit.clarify
name: speckit-clarify
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
version: 1.0.0
depends-on:
- speckit.specify
- speckit-specify
handoffs:
- label: Build Technical Plan
agent: speckit.plan
agent: speckit-plan
prompt: Create a plan for the spec. I am building with...
---
@@ -28,7 +28,7 @@ You are the **Antigravity Ambiguity Buster**. Your role is to interrogate specif
Goal: Detect and reduce ambiguity or missing decision points in the active feature specification and record the clarifications directly in the spec file.
Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/speckit.plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases.
Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/speckit-plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases.
Execution steps:
@@ -36,7 +36,7 @@ Execution steps:
- `FEATURE_DIR`
- `FEATURE_SPEC`
- (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.)
- If JSON parsing fails, abort and instruct user to re-run `/speckit.specify` or verify feature branch environment.
- If JSON parsing fails, abort and instruct user to re-run `/speckit-specify` or verify feature branch environment.
- For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\\''m Groot' (or double-quote if possible: "I'm Groot").
2. Load the current spec file. Perform a structured ambiguity & coverage scan using this taxonomy. For each category, mark status: Clear / Partial / Missing. Produce an internal coverage map used for prioritization (do not output raw map unless no questions will be asked).
@@ -175,13 +175,13 @@ Execution steps:
- Path to updated spec.
- Sections touched (list names).
- Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact).
- If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit.plan` or run `/speckit.clarify` again later post-plan.
- If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit-plan` or run `/speckit-clarify` again later post-plan.
- Suggested next command.
Behavior rules:
- If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding.
- If spec file missing, instruct user to run `/speckit.specify` first (do not create a new spec here).
- If spec file missing, instruct user to run `/speckit-specify` first (do not create a new spec here).
- Never exceed 5 total asked questions (clarification retries for a single question do not count as new questions).
- Avoid speculative tech stack questions unless the absence blocks functional clarity.
- Respect user early termination signals ("stop", "done", "proceed").
@@ -1,10 +1,10 @@
---
name: speckit.constitution
name: speckit-constitution
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
version: 1.0.0
handoffs:
- label: Build Specification
agent: speckit.specify
agent: speckit-specify
prompt: Implement the feature specification based on the updated constitution. I want to build...
---
@@ -1,5 +1,5 @@
---
name: speckit.diff
name: speckit-diff
description: Compare two versions of a spec or plan to highlight changes.
version: 1.0.0
depends-on: []
@@ -1,9 +1,9 @@
---
name: speckit.implement
name: speckit-implement
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md (with Ironclad Anti-Regression Protocols)
version: 1.0.0
depends-on:
- speckit.tasks
- speckit-tasks
---
## User Input
@@ -245,4 +245,4 @@ At the start of execution and after every 3 modifications:
---
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit.tasks` first to regenerate the task list.
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit-tasks` first to regenerate the task list.
@@ -1,5 +1,5 @@
---
name: speckit.migrate
name: speckit-migrate
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
version: 1.0.0
depends-on: []
@@ -1,16 +1,16 @@
---
name: speckit.plan
name: speckit-plan
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
version: 1.0.0
depends-on:
- speckit.specify
- speckit-specify
handoffs:
- label: Create Tasks
agent: speckit.tasks
agent: speckit-tasks
prompt: Break the plan into tasks
send: true
- label: Create Checklist
agent: speckit.checklist
agent: speckit-checklist
prompt: Create a checklist for the following domain...
---
@@ -3,7 +3,7 @@
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
**Note**: This template is filled in by the `/speckit-plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
## Summary
@@ -39,12 +39,12 @@
```text
specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
├── plan.md # This file (/speckit-plan command output)
├── research.md # Phase 0 output (/speckit-plan command)
├── data-model.md # Phase 1 output (/speckit-plan command)
├── quickstart.md # Phase 1 output (/speckit-plan command)
├── contracts/ # Phase 1 output (/speckit-plan command)
└── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan)
```
### Source Code (repository root)
@@ -1,10 +1,10 @@
---
name: speckit.quizme
name: speckit-quizme
description: Challenge the specification with Socratic questioning to identify logical gaps, unhandled edge cases, and robustness issues.
version: 1.0.0
handoffs:
- label: Clarify Spec Requirements
agent: speckit.clarify
agent: speckit-clarify
prompt: Clarify specification requirements
---
@@ -24,7 +24,7 @@ You are the **Antigravity Red Teamer**. Your role is to play the "Socratic Teach
### Outline
Goal: Act as a "Red Team" or "Socratic Teacher" to challenge the current feature specification. Unlike `speckit.clarify` (which looks for missing definitions), `speckit.quizme` looks for logical fallacies, race conditions, naive assumptions, and "happy path" bias.
Goal: Act as a "Red Team" or "Socratic Teacher" to challenge the current feature specification. Unlike `speckit-clarify` (which looks for missing definitions), `speckit-quizme` looks for logical fallacies, race conditions, naive assumptions, and "happy path" bias.
Execution steps:
@@ -1,5 +1,5 @@
---
name: speckit.reviewer
name: speckit-reviewer
description: Perform code review with actionable feedback and suggestions.
version: 1.0.0
depends-on: []
@@ -1,9 +1,9 @@
---
name: speckit.security-audit
name: speckit-security-audit
description: Perform a security-focused audit of the codebase against OWASP Top 10, CASL authorization, and LCBP3-DMS security requirements.
version: 1.0.0
depends-on:
- speckit.checker
- speckit-checker
---
## Role
@@ -1,13 +1,13 @@
---
name: speckit.specify
name: speckit-specify
description: Create or update the feature specification from a natural language feature description.
version: 1.0.0
handoffs:
- label: Build Technical Plan
agent: speckit.plan
agent: speckit-plan
prompt: Create a plan for the spec. I am building with...
- label: Clarify Spec Requirements
agent: speckit.clarify
agent: speckit-clarify
prompt: Clarify specification requirements
send: true
---
@@ -28,7 +28,7 @@ You are the **Antigravity Domain Scribe**. Your role is to translate natural lan
### Outline
The text the user typed after `/speckit.specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{{args}}` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
The text the user typed after `/speckit-specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{{args}}` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
Given that feature description, do this:
@@ -143,7 +143,7 @@ Given that feature description, do this:
## Notes
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
- Items marked incomplete require spec updates before `/speckit-clarify` or `/speckit-plan`
```
b. **Run Validation Check**: Review the spec against each checklist item:
@@ -196,7 +196,7 @@ Given that feature description, do this:
d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit-clarify` or `/speckit-plan`).
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
@@ -1,5 +1,5 @@
---
name: speckit.status
name: speckit-status
description: Display a dashboard showing feature status, completion percentage, and blockers.
version: 1.0.0
depends-on: []
@@ -1,16 +1,16 @@
---
name: speckit.tasks
name: speckit-tasks
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
version: 1.0.0
depends-on:
- speckit.plan
- speckit-plan
handoffs:
- label: Analyze For Consistency
agent: speckit.analyze
agent: speckit-analyze
prompt: Run a project analysis for consistency
send: true
- label: Implement Project
agent: speckit.implement
agent: speckit-implement
prompt: Start the implementation in phases
send: true
---
@@ -29,7 +29,7 @@ description: "Task list template for feature implementation"
============================================================================
IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only.
The /speckit.tasks command MUST replace these with actual tasks based on:
The /speckit-tasks command MUST replace these with actual tasks based on:
- User stories from spec.md (with their priorities P1, P2, P3...)
- Feature requirements from plan.md
- Entities from data-model.md
@@ -1,9 +1,9 @@
---
name: speckit.taskstoissues
name: speckit-taskstoissues
description: Convert existing tasks into actionable, dependency-ordered issues for the feature based on available design artifacts.
version: 1.1.0
depends-on:
- speckit.tasks
- speckit-tasks
tools: ['github/github-mcp-server/issue_write']
---
@@ -112,7 +112,7 @@ Convert all tasks from `tasks.md` into well-structured issues on the appropriate
---
_Auto-generated by speckit.taskstoissues from `tasks.md`_
_Auto-generated by speckit-taskstoissues from `tasks.md`_
```
7. **Apply Labels** — Assign labels based on task metadata:
@@ -1,5 +1,5 @@
---
name: speckit.tester
name: speckit-tester
description: Execute tests, measure coverage, and report results.
version: 1.0.0
depends-on: []
@@ -1,9 +1,9 @@
---
name: speckit.validate
name: speckit-validate
description: Validate that implementation matches specification requirements.
version: 1.0.0
depends-on:
- speckit.implement
- speckit-implement
---
## User Input
@@ -2,79 +2,79 @@
description: Run the full speckit pipeline from specification to analysis in one command.
---
# Workflow: speckit.all
# Workflow: speckit-all
This meta-workflow orchestrates the **complete development lifecycle**, from specification through implementation and validation. For the preparation-only pipeline (steps 1-5), use `/speckit.prepare` instead.
This meta-workflow orchestrates the **complete development lifecycle**, from specification through implementation and validation. For the preparation-only pipeline (steps 1-5), use `/speckit-prepare` instead.
## Preparation Phase (Steps 1-5)
1. **Specify** (`/speckit.specify`):
- Use the `view_file` tool to read: `.agents/skills/speckit.specify/SKILL.md`
1. **Specify** (`/speckit-specify`):
- Use the `view_file` tool to read: `.agents/skills/speckit-specify/SKILL.md`
- Execute with user's feature description
- Creates: `spec.md`
2. **Clarify** (`/speckit.clarify`):
- Use the `view_file` tool to read: `.agents/skills/speckit.clarify/SKILL.md`
2. **Clarify** (`/speckit-clarify`):
- Use the `view_file` tool to read: `.agents/skills/speckit-clarify/SKILL.md`
- Execute to resolve ambiguities
- Updates: `spec.md`
3. **Plan** (`/speckit.plan`):
- Use the `view_file` tool to read: `.agents/skills/speckit.plan/SKILL.md`
3. **Plan** (`/speckit-plan`):
- Use the `view_file` tool to read: `.agents/skills/speckit-plan/SKILL.md`
- Execute to create technical design
- Creates: `plan.md`
4. **Tasks** (`/speckit.tasks`):
- Use the `view_file` tool to read: `.agents/skills/speckit.tasks/SKILL.md`
4. **Tasks** (`/speckit-tasks`):
- Use the `view_file` tool to read: `.agents/skills/speckit-tasks/SKILL.md`
- Execute to generate task breakdown
- Creates: `tasks.md`
5. **Analyze** (`/speckit.analyze`):
- Use the `view_file` tool to read: `.agents/skills/speckit.analyze/SKILL.md`
5. **Analyze** (`/speckit-analyze`):
- Use the `view_file` tool to read: `.agents/skills/speckit-analyze/SKILL.md`
- Execute to validate consistency across spec, plan, and tasks
- Output: Analysis report
- **Gate**: If critical issues found, stop and fix before proceeding
## Implementation Phase (Steps 6-7)
6. **Implement** (`/speckit.implement`):
- Use the `view_file` tool to read: `.agents/skills/speckit.implement/SKILL.md`
6. **Implement** (`/speckit-implement`):
- Use the `view_file` tool to read: `.agents/skills/speckit-implement/SKILL.md`
- Execute all tasks from `tasks.md` with anti-regression protocols
- Output: Working implementation
7. **Check** (`/speckit.checker`):
- Use the `view_file` tool to read: `.agents/skills/speckit.checker/SKILL.md`
7. **Check** (`/speckit-checker`):
- Use the `view_file` tool to read: `.agents/skills/speckit-checker/SKILL.md`
- Run static analysis (linters, type checkers, security scanners)
- Output: Checker report
## Verification Phase (Steps 8-10)
8. **Test** (`/speckit.tester`):
- Use the `view_file` tool to read: `.agents/skills/speckit.tester/SKILL.md`
8. **Test** (`/speckit-tester`):
- Use the `view_file` tool to read: `.agents/skills/speckit-tester/SKILL.md`
- Run tests with coverage
- Output: Test + coverage report
9. **Review** (`/speckit.reviewer`):
- Use the `view_file` tool to read: `.agents/skills/speckit.reviewer/SKILL.md`
9. **Review** (`/speckit-reviewer`):
- Use the `view_file` tool to read: `.agents/skills/speckit-reviewer/SKILL.md`
- Perform code review
- Output: Review report with findings
10. **Validate** (`/speckit.validate`):
- Use the `view_file` tool to read: `.agents/skills/speckit.validate/SKILL.md`
10. **Validate** (`/speckit-validate`):
- Use the `view_file` tool to read: `.agents/skills/speckit-validate/SKILL.md`
- Verify implementation matches spec requirements
- Output: Validation report (pass/fail)
## Usage
```
/speckit.all "Build a user authentication system with OAuth2 support"
/speckit-all "Build a user authentication system with OAuth2 support"
```
## Pipeline Comparison
| Pipeline | Steps | Use When |
| ------------------ | ------------------------- | -------------------------------------- |
| `/speckit.prepare` | 1-5 (Specify → Analyze) | Planning only — you'll implement later |
| `/speckit.all` | 1-10 (Specify → Validate) | Full lifecycle in one pass |
| `/speckit-prepare` | 1-5 (Specify → Analyze) | Planning only — you'll implement later |
| `/speckit-all` | 1-10 (Specify → Validate) | Full lifecycle in one pass |
## On Error
@@ -82,4 +82,4 @@ If any step fails, stop the pipeline and report:
- Which step failed
- The error message
- Suggested remediation (e.g., "Run `/speckit.clarify` to resolve ambiguities before continuing")
- Suggested remediation (e.g., "Run `/speckit-clarify` to resolve ambiguities before continuing")
@@ -2,13 +2,13 @@
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
---
# Workflow: speckit.constitution
# Workflow: speckit-constitution
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.constitution/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-constitution/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -2,14 +2,14 @@
description: Create or update the feature specification from a natural language feature description.
---
# Workflow: speckit.specify
# Workflow: speckit-specify
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
- This is typically the starting point of a new feature.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.specify/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-specify/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -2,17 +2,17 @@
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
---
# Workflow: speckit.clarify
# Workflow: speckit-clarify
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.clarify/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-clarify/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification
- If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,17 +2,17 @@
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
---
# Workflow: speckit.plan
# Workflow: speckit-plan
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.plan/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-plan/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification
- If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,18 +2,18 @@
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
---
# Workflow: speckit.tasks
# Workflow: speckit-tasks
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.tasks/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tasks/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `plan.md` is missing: Run `/speckit.plan` first
- If `spec.md` is missing: Run `/speckit.specify` first
- If `plan.md` is missing: Run `/speckit-plan` first
- If `spec.md` is missing: Run `/speckit-specify` first
@@ -4,19 +4,19 @@ description: Perform a non-destructive cross-artifact consistency and quality an
// turbo-all
# Workflow: speckit.analyze
# Workflow: speckit-analyze
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.analyze/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-analyze/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first
- If `plan.md` is missing: Run `/speckit.plan` first
- If `tasks.md` is missing: Run `/speckit.tasks` first
- If `spec.md` is missing: Run `/speckit-specify` first
- If `plan.md` is missing: Run `/speckit-plan` first
- If `tasks.md` is missing: Run `/speckit-tasks` first
@@ -2,19 +2,19 @@
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md
---
# Workflow: speckit.implement
# Workflow: speckit-implement
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.implement/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-implement/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first
- If `plan.md` is missing: Run `/speckit.plan` first
- If `spec.md` is missing: Run `/speckit.specify` first
- If `tasks.md` is missing: Run `/speckit-tasks` first
- If `plan.md` is missing: Run `/speckit-plan` first
- If `spec.md` is missing: Run `/speckit-specify` first
@@ -4,13 +4,13 @@ description: Run static analysis tools and aggregate results.
// turbo-all
# Workflow: speckit.checker
# Workflow: speckit-checker
1. **Context Analysis**:
- The user may specify paths to check or run on entire project.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.checker/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checker/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -4,13 +4,13 @@ description: Execute tests, measure coverage, and report results.
// turbo-all
# Workflow: speckit.tester
# Workflow: speckit-tester
1. **Context Analysis**:
- The user may specify test paths, options, or just run all tests.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.tester/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tester/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -2,13 +2,13 @@
description: Perform code review with actionable feedback and suggestions.
---
# Workflow: speckit.reviewer
# Workflow: speckit-reviewer
1. **Context Analysis**:
- The user may specify files to review, "staged" for git staged changes, or "branch" for branch diff.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.reviewer/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-reviewer/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -2,18 +2,18 @@
description: Validate that implementation matches specification requirements.
---
# Workflow: speckit.validate
# Workflow: speckit-validate
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.validate/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-validate/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first
- If implementation not started: Run `/speckit.implement` first
- If `tasks.md` is missing: Run `/speckit-tasks` first
- If implementation not started: Run `/speckit-implement` first
@@ -2,26 +2,26 @@
description: Execute the full preparation pipeline (Specify -> Clarify -> Plan -> Tasks -> Analyze) in sequence.
---
# Workflow: speckit.prepare
# Workflow: speckit-prepare
This workflow orchestrates the sequential execution of the Speckit preparation phase skills (02-06).
1. **Step 1: Specify (Skill 02)**
- Goal: Create or update the `spec.md` based on user input.
- Action: Read and execute `.agents/skills/speckit.specify/SKILL.md`.
- Action: Read and execute `.agents/skills/speckit-specify/SKILL.md`.
2. **Step 2: Clarify (Skill 03)**
- Goal: Refine the `spec.md` by identifying and resolving ambiguities.
- Action: Read and execute `.agents/skills/speckit.clarify/SKILL.md`.
- Action: Read and execute `.agents/skills/speckit-clarify/SKILL.md`.
3. **Step 3: Plan (Skill 04)**
- Goal: Generate `plan.md` from the finalized spec.
- Action: Read and execute `.agents/skills/speckit.plan/SKILL.md`.
- Action: Read and execute `.agents/skills/speckit-plan/SKILL.md`.
4. **Step 4: Tasks (Skill 05)**
- Goal: Generate actionable `tasks.md` from the plan.
- Action: Read and execute `.agents/skills/speckit.tasks/SKILL.md`.
- Action: Read and execute `.agents/skills/speckit-tasks/SKILL.md`.
5. **Step 5: Analyze (Skill 06)**
- Goal: Validate consistency across all design artifacts (spec, plan, tasks).
- Action: Read and execute `.agents/skills/speckit.analyze/SKILL.md`.
- Action: Read and execute `.agents/skills/speckit-analyze/SKILL.md`.
@@ -2,17 +2,17 @@
description: Generate a custom checklist for the current feature based on user requirements.
---
# Workflow: speckit.checklist
# Workflow: speckit-checklist
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.checklist/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checklist/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification
- If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,13 +2,13 @@
description: Compare two versions of a spec or plan to highlight changes.
---
# Workflow: speckit.diff
# Workflow: speckit-diff
1. **Context Analysis**:
- The user has provided an input prompt (optional file paths or version references).
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.diff/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-diff/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -16,4 +16,4 @@ description: Compare two versions of a spec or plan to highlight changes.
4. **On Error**:
- If no files to compare: Use current feature's `spec.md` vs git HEAD
- If `spec.md` doesn't exist: Run `/speckit.specify` first
- If `spec.md` doesn't exist: Run `/speckit-specify` first
@@ -2,13 +2,13 @@
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
---
# Workflow: speckit.migrate
# Workflow: speckit-migrate
1. **Context Analysis**:
- The user has provided an input prompt (path to analyze, feature name).
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.migrate/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-migrate/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
@@ -4,17 +4,17 @@ description: Challenge the specification with Socratic questioning to identify l
// turbo-all
# Workflow: speckit.quizme
# Workflow: speckit-quizme
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.quizme/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-quizme/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If required files don't exist, inform the user which prerequisite workflow to run first (e.g., `/speckit.specify` to create `spec.md`).
- If required files don't exist, inform the user which prerequisite workflow to run first (e.g., `/speckit-specify` to create `spec.md`).
@@ -4,17 +4,17 @@ description: Display a dashboard showing feature status, completion percentage,
// turbo-all
# Workflow: speckit.status
# Workflow: speckit-status
1. **Context Analysis**:
- The user may optionally specify a feature to focus on.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.status/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-status/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If no features exist: Report "No features found. Run `/speckit.specify` to create your first feature."
- If no features exist: Report "No features found. Run `/speckit-specify` to create your first feature."
@@ -2,17 +2,17 @@
description: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts.
---
# Workflow: speckit.taskstoissues
# Workflow: speckit-taskstoissues
1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.taskstoissues/SKILL.md`
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-taskstoissues/SKILL.md`
3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first
- If `tasks.md` is missing: Run `/speckit-tasks` first
@@ -88,14 +88,31 @@ export class DocumentNumberingController {
})
@RequirePermission('correspondence.read')
async previewNumber(@Body() dto: PreviewNumberDto) {
// ADR-019: Resolve UUID→INT for project and organization IDs
const resolvedProjectId = await this.numberingService.resolveIdForPreview(
'project',
dto.projectId
);
const resolvedOriginatorId =
await this.numberingService.resolveIdForPreview(
'organization',
dto.originatorOrganizationId
);
const resolvedRecipientId = dto.recipientOrganizationId
? await this.numberingService.resolveIdForPreview(
'organization',
dto.recipientOrganizationId
)
: undefined;
return this.numberingService.previewNumber({
projectId: dto.projectId,
originatorOrganizationId: dto.originatorOrganizationId,
projectId: resolvedProjectId,
originatorOrganizationId: resolvedOriginatorId,
typeId: dto.correspondenceTypeId,
subTypeId: dto.subTypeId,
rfaTypeId: dto.rfaTypeId,
disciplineId: dto.disciplineId,
recipientOrganizationId: dto.recipientOrganizationId,
recipientOrganizationId: resolvedRecipientId,
year: dto.year,
customTokens: dto.customTokens,
});
@@ -1,18 +1,16 @@
// File: src/modules/document-numbering/dto/preview-number.dto.ts
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsInt, IsOptional, IsObject } from 'class-validator';
import { IsInt, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
import { Type } from 'class-transformer';
export class PreviewNumberDto {
@ApiProperty({ description: 'Project ID' })
@IsInt()
@Type(() => Number)
projectId!: number;
@ApiProperty({ description: 'Project ID or UUID' })
@IsNotEmpty()
projectId!: number | string;
@ApiProperty({ description: 'Originator organization ID' })
@IsInt()
@Type(() => Number)
originatorOrganizationId!: number;
@ApiProperty({ description: 'Originator organization ID or UUID' })
@IsNotEmpty()
originatorOrganizationId!: number | string;
@ApiProperty({ description: 'Correspondence type ID' })
@IsInt()
@@ -43,11 +41,9 @@ export class PreviewNumberDto {
@Type(() => Number)
year?: number;
@ApiPropertyOptional({ description: 'Recipient organization ID' })
@ApiPropertyOptional({ description: 'Recipient organization ID or UUID' })
@IsOptional()
@IsInt()
@Type(() => Number)
recipientOrganizationId?: number;
recipientOrganizationId?: number | string;
@ApiPropertyOptional({ description: 'Custom tokens' })
@IsOptional()
@@ -26,6 +26,7 @@ import { GenerateNumberContext } from '../interfaces/document-numbering.interfac
import { ReserveNumberDto } from '../dto/reserve-number.dto';
import { ConfirmReservationDto } from '../dto/confirm-reservation.dto';
import { Project } from '../../project/entities/project.entity';
import { Organization } from '../../organization/entities/organization.entity';
@Injectable()
export class DocumentNumberingService {
@@ -66,6 +67,33 @@ export class DocumentNumberingService {
return project.id;
}
/**
* ADR-019: Public facade for controllers to resolve project/organization IDs
*/
async resolveIdForPreview(
type: 'project' | 'organization',
id: number | string
): Promise<number> {
if (type === 'project') return this.resolveProjectId(id);
return this.resolveOrganizationId(id);
}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.entityManager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
async generateNextNumber(
ctx: GenerateNumberContext
): Promise<{ number: string; auditId: number }> {
@@ -13,6 +13,7 @@ import { AsBuiltDrawingRevision } from './entities/asbuilt-drawing-revision.enti
import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity';
import { Project } from '../project/entities/project.entity';
// DTOs
import { CreateAsBuiltDrawingDto } from './dto/create-asbuilt-drawing.dto';
@@ -39,6 +40,22 @@ export class AsBuiltDrawingService {
private dataSource: DataSource
) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/**
* สร้าง AS Built Drawing ใหม่ พร้อม Revision แรก (Rev 0)
*/
@@ -73,9 +90,14 @@ export class AsBuiltDrawingService {
});
}
// ADR-019: Resolve UUID→INT
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 3. Create Master AS Built Drawing
const asBuiltDrawing = queryRunner.manager.create(AsBuiltDrawing, {
projectId: createDto.projectId,
projectId: internalProjectId,
drawingNumber: createDto.drawingNumber,
mainCategoryId: createDto.mainCategoryId,
subCategoryId: createDto.subCategoryId,
@@ -12,6 +12,7 @@ import { ContractDrawing } from './entities/contract-drawing.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity';
import { Contract } from '../contract/entities/contract.entity';
import { Project } from '../project/entities/project.entity';
// DTOs
import { CreateContractDrawingDto } from './dto/create-contract-drawing.dto';
@@ -36,6 +37,22 @@ export class ContractDrawingService {
private dataSource: DataSource
) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/**
* Resolve issueDate from contract.startDate for file storage path
* Fallback: contract.startDate → current date
@@ -54,10 +71,13 @@ export class ContractDrawingService {
* - ผูกไฟล์แนบและ Commit ไฟล์จาก Temp -> Permanent
*/
async create(createDto: CreateContractDrawingDto, user: User) {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(createDto.projectId);
// 1. ตรวจสอบเลขที่แบบซ้ำ (Unique per Project)
const exists = await this.drawingRepo.findOne({
where: {
projectId: createDto.projectId,
projectId: internalProjectId,
contractDrawingNo: createDto.contractDrawingNo,
},
});
@@ -83,7 +103,7 @@ export class ContractDrawingService {
// 3. สร้าง Entity
const drawing = queryRunner.manager.create(ContractDrawing, {
projectId: createDto.projectId,
projectId: internalProjectId,
contractDrawingNo: createDto.contractDrawingNo,
title: createDto.title,
mapCatId: createDto.mapCatId, // Updated
@@ -98,9 +118,8 @@ export class ContractDrawingService {
// 4. Commit Files (ย้ายไฟล์จริง)
if (createDto.attachmentIds?.length) {
// ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง
const issueDate = await this.resolveIssueDateByProject(
createDto.projectId
);
const issueDate =
await this.resolveIssueDateByProject(internalProjectId);
await this.fileStorageService.commit(
createDto.attachmentIds.map(String),
{ issueDate, documentType: 'ContractDrawing' }
@@ -13,6 +13,7 @@ import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
import { ContractDrawing } from './entities/contract-drawing.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity';
import { Project } from '../project/entities/project.entity';
// DTOs
import { CreateShopDrawingDto } from './dto/create-shop-drawing.dto';
@@ -39,6 +40,22 @@ export class ShopDrawingService {
private dataSource: DataSource
) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/**
* สร้าง Shop Drawing ใหม่ พร้อม Revision แรก (Rev 0)
*/
@@ -73,9 +90,14 @@ export class ShopDrawingService {
});
}
// ADR-019: Resolve UUID→INT
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 3. Create Master Shop Drawing
const shopDrawing = queryRunner.manager.create(ShopDrawing, {
projectId: createDto.projectId,
projectId: internalProjectId,
drawingNumber: createDto.drawingNumber,
mainCategoryId: createDto.mainCategoryId,
subCategoryId: createDto.subCategoryId,
+11 -3
View File
@@ -11,10 +11,18 @@ import {
} from 'class-validator';
export class CreateRfaDto {
@ApiProperty({ description: 'ID ของโครงการ', example: 1 })
@IsInt()
@ApiProperty({ description: 'ID or UUID ของโครงการ', example: 1 })
@IsNotEmpty()
projectId!: number;
projectId!: number | string;
@ApiProperty({ description: 'Contract ID or UUID', required: false })
@IsString()
@IsOptional()
contractId?: string;
@ApiProperty({ description: 'To Organization ID or UUID', required: false })
@IsOptional()
toOrganizationId?: number | string;
@ApiProperty({ description: 'ID ของประเภท RFA', example: 1 })
@IsInt()
+41 -4
View File
@@ -12,6 +12,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm';
// Entities
import { Project } from '../project/entities/project.entity';
import { Organization } from '../organization/entities/organization.entity';
import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity';
import { Correspondence } from '../correspondence/entities/correspondence.entity';
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
@@ -81,7 +83,42 @@ export class RfaService {
private searchService: SearchService
) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.dataSource.manager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
async create(createDto: CreateRfaDto, user: User) {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(createDto.projectId);
const rfaType = await this.rfaTypeRepo.findOne({
where: { id: createDto.rfaTypeId },
});
@@ -115,7 +152,7 @@ export class RfaService {
// [UPDATED] Generate Document Number with Discipline
const docNumber = await this.numberingService.generateNextNumber({
projectId: createDto.projectId,
projectId: internalProjectId,
originatorOrganizationId: userOrgId,
typeId: createDto.rfaTypeId,
disciplineId: createDto.disciplineId ?? 0, // ✅ ส่ง disciplineId ไปด้วย (0 ถ้าไม่มี)
@@ -142,7 +179,7 @@ export class RfaService {
const correspondence = queryRunner.manager.create(Correspondence, {
correspondenceNumber: docNumber.number,
correspondenceTypeId: createDto.rfaTypeId,
projectId: createDto.projectId,
projectId: internalProjectId,
originatorId: userOrgId,
isInternal: false,
createdBy: user.user_id,
@@ -219,7 +256,7 @@ export class RfaService {
'rfa',
savedRfa.id.toString(),
{
projectId: createDto.projectId,
projectId: internalProjectId,
originatorId: userOrgId,
disciplineId: createDto.disciplineId,
initiatorId: user.user_id,
@@ -240,7 +277,7 @@ export class RfaService {
title: createDto.subject,
description: createDto.description,
status: 'DRAFT',
projectId: createDto.projectId,
projectId: internalProjectId,
createdAt: new Date(),
})
.catch((err) => this.logger.error(`Indexing failed: ${err}`));
@@ -16,6 +16,7 @@ import { Correspondence } from '../correspondence/entities/correspondence.entity
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
import { Project } from '../project/entities/project.entity';
@Injectable()
export class TransmittalService {
@@ -34,6 +35,22 @@ export class TransmittalService {
private dataSource: DataSource
) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
async create(createDto: CreateTransmittalDto, user: User) {
// 1. Get Transmittal Type (Assuming Code '901' or 'TRN')
const type = await this.typeRepo.findOne({
@@ -58,9 +75,14 @@ export class TransmittalService {
}
try {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 2. Generate Number
const docNumber = await this.numberingService.generateNextNumber({
projectId: createDto.projectId,
projectId: internalProjectId,
originatorOrganizationId: user.primaryOrganizationId,
typeId: type.id,
year: new Date().getFullYear(),
@@ -74,7 +96,7 @@ export class TransmittalService {
const correspondence = queryRunner.manager.create(Correspondence, {
correspondenceNumber: docNumber.number,
correspondenceTypeId: type.id,
projectId: createDto.projectId,
projectId: internalProjectId,
originatorId: user.primaryOrganizationId,
isInternal: false,
createdBy: user.user_id,
@@ -5,7 +5,6 @@ import {
MinLength,
IsOptional,
IsBoolean,
IsInt,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
@@ -44,10 +43,12 @@ export class CreateUserDto {
@IsOptional()
lineId?: string;
@ApiPropertyOptional({ description: 'Primary Organization ID', example: 1 })
@IsInt()
@ApiPropertyOptional({
description: 'Primary Organization ID or UUID',
example: 1,
})
@IsOptional()
primaryOrganizationId?: number; // รับเป็น ID ของ Organization
primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
@ApiPropertyOptional({ description: 'Is user active?', default: true })
@IsBoolean()
@@ -16,11 +16,9 @@ export class SearchUserDto {
@Type(() => Number)
roleId?: number;
@ApiPropertyOptional({ description: 'Filter by Organization ID' })
@ApiPropertyOptional({ description: 'Filter by Organization ID or UUID' })
@IsOptional()
@IsInt()
@Type(() => Number)
primaryOrganizationId?: number;
primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
@ApiPropertyOptional({ description: 'Page number', default: 1 })
@IsOptional()
+40 -2
View File
@@ -17,6 +17,7 @@ import { Role } from './entities/role.entity';
import { Permission } from './entities/permission.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Organization } from '../organization/entities/organization.entity';
@Injectable()
export class UserService {
@@ -30,13 +31,35 @@ export class UserService {
@Inject(CACHE_MANAGER) private cacheManager: Cache
) {}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.usersRepository.manager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
// 1. สร้างผู้ใช้ (Hash Password ก่อนบันทึก)
async create(createUserDto: CreateUserDto): Promise<User> {
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(createUserDto.password, salt);
// ADR-019: Resolve UUID→INT for primaryOrganizationId
const resolvedOrgId = createUserDto.primaryOrganizationId
? await this.resolveOrganizationId(createUserDto.primaryOrganizationId)
: undefined;
const newUser = this.usersRepository.create({
...createUserDto,
primaryOrganizationId: resolvedOrgId,
password: hashedPassword,
});
@@ -91,8 +114,12 @@ export class UserService {
}
if (primaryOrganizationId) {
// ADR-019: Resolve UUID→INT for filtering
const resolvedOrgId = await this.resolveOrganizationId(
primaryOrganizationId
);
query.andWhere('user.primaryOrganizationId = :orgId', {
orgId: primaryOrganizationId,
orgId: resolvedOrgId,
});
}
@@ -164,7 +191,18 @@ export class UserService {
updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
}
const updatedUser = this.usersRepository.merge(user, updateUserDto);
// ADR-019: Resolve UUID→INT for primaryOrganizationId before merge
const resolvedDto: Record<string, unknown> = { ...updateUserDto };
if (updateUserDto.primaryOrganizationId !== undefined) {
resolvedDto.primaryOrganizationId = await this.resolveOrganizationId(
updateUserDto.primaryOrganizationId
);
}
const updatedUser = this.usersRepository.merge(
user,
resolvedDto as Partial<User>
);
const savedUser = await this.usersRepository.save(updatedUser);
// ⚠️ สำคัญ: เมื่อมีการแก้ไขข้อมูล User ต้องเคลียร์ Cache สิทธิ์เสมอ
@@ -44,7 +44,7 @@ export default function UsersPage() {
const { data: users, isLoading } = useUsers({
search: search || undefined,
primaryOrganizationId: selectedOrgId ? parseInt(selectedOrgId) : undefined,
primaryOrganizationId: selectedOrgId ?? undefined,
});
const { data: organizations = [] } = useOrganizations();
@@ -94,7 +94,7 @@ export default function UsersPage() {
header: "Organization",
cell: ({ row }) => {
const orgId = row.original.primaryOrganizationId;
const org = (organizations as Organization[]).find((o) => o.id === orgId);
const org = (organizations as Organization[]).find((o) => (o.id ?? o.uuid) === orgId?.toString() || o.uuid === orgId?.toString());
return org ? org.organizationCode : "-";
},
},
@@ -186,7 +186,7 @@ export default function UsersPage() {
<SelectContent>
<SelectItem value="all">All Organizations</SelectItem>
{Array.isArray(organizations) && (organizations as Organization[]).map((org) => (
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}>
<SelectItem key={org.uuid} value={org.uuid}>
{org.organizationCode} - {org.organizationName}
</SelectItem>
))}
+1 -1
View File
@@ -14,7 +14,7 @@ function RFAsContent() {
const page = parseInt(searchParams.get('page') || '1');
const statusId = searchParams.get('status') ? parseInt(searchParams.get('status')!) : undefined;
const search = searchParams.get('search') || undefined;
const projectId = searchParams.get('projectId') ? parseInt(searchParams.get('projectId')!) : undefined;
const projectId = searchParams.get('projectId') || undefined; // ADR-019: Pass UUID string directly
const revisionStatus = (searchParams.get('revisionStatus') as 'CURRENT' | 'ALL' | 'OLD') || 'CURRENT';
+6 -9
View File
@@ -36,7 +36,7 @@ const userSchema = z.object({
confirmPassword: z.string().optional(),
isActive: z.boolean().optional(),
lineId: z.string().optional(),
primaryOrganizationId: z.number().optional(),
primaryOrganizationId: z.string().optional(),
roleIds: z.array(z.number()).optional(),
}).refine((data) => {
// If password is provided (creating or resetting), confirmPassword must match
@@ -107,7 +107,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
lastName: user.lastName,
isActive: user.isActive,
lineId: user.lineId || "",
primaryOrganizationId: user.primaryOrganizationId,
primaryOrganizationId: user.primaryOrganizationId?.toString(),
roleIds: user.roles?.map((r: any) => r.roleId) || [],
password: "",
confirmPassword: ""
@@ -221,22 +221,19 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
<div>
<Label>Primary Organization</Label>
<Select
value={watch("primaryOrganizationId")?.toString()}
value={watch("primaryOrganizationId") ?? undefined}
onValueChange={(val) =>
setValue("primaryOrganizationId", parseInt(val))
setValue("primaryOrganizationId", val)
}
>
<SelectTrigger>
<SelectValue placeholder="Select Organization" />
</SelectTrigger>
<SelectContent>
{/* TODO: ADR-019 — Backend DTO needs to accept UUID for primaryOrganization.
Currently using org.id which is excluded from API responses.
Temporary: org.id may still exist in some query responses. */}
{organizations?.map((org: any) => (
<SelectItem
key={org.uuid ?? org.id}
value={(org.id ?? 0).toString()}
key={org.uuid}
value={org.uuid}
>
{org.organizationCode} - {org.organizationName}
</SelectItem>
+6 -6
View File
@@ -80,7 +80,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
const router = useRouter();
// Hooks
const { data: contractCategories } = useContractDrawingCategories();
const { data: contractCategories } = useContractDrawingCategories(projectId);
const { data: shopMainCats } = useShopMainCategories(projectId);
const [selectedShopMainCat, setSelectedShopMainCat] = useState<number | undefined>();
@@ -202,7 +202,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger>
<SelectContent>
{contractCategories?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem>
<SelectItem key={c.id} value={String(c.id)}>{c.catName || c.catCode || c.name}</SelectItem>
))}
</SelectContent>
</Select>
@@ -253,7 +253,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger>
<SelectContent>
{shopMainCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem>
<SelectItem key={c.id} value={String(c.id)}>{c.mainCategoryName || c.mainCategoryCode || c.name}</SelectItem>
))}
</SelectContent>
</Select>
@@ -269,7 +269,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger>
<SelectContent>
{shopSubCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem>
<SelectItem key={c.id} value={String(c.id)}>{c.subCategoryName || c.subCategoryCode || c.name}</SelectItem>
))}
</SelectContent>
</Select>
@@ -323,7 +323,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger>
<SelectContent>
{shopMainCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem>
<SelectItem key={c.id} value={String(c.id)}>{c.mainCategoryName || c.mainCategoryCode || c.name}</SelectItem>
))}
</SelectContent>
</Select>
@@ -339,7 +339,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger>
<SelectContent>
{shopSubCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem>
<SelectItem key={c.id} value={String(c.id)}>{c.subCategoryName || c.subCategoryCode || c.name}</SelectItem>
))}
</SelectContent>
</Select>
@@ -37,7 +37,7 @@ const VARIABLES = [
export interface TemplateEditorProps {
template?: NumberingTemplate;
projectId: number;
projectId: number | string;
projectName: string;
/* eslint-disable @typescript-eslint/no-explicit-any */
correspondenceTypes: any[];
+7 -7
View File
@@ -30,14 +30,14 @@ const rfaItemSchema = z.object({
unit: z.string().min(1, "Unit is required"),
});
const rfaSchema = z.object({
contractId: z.number().min(1, "Contract is required"),
contractId: z.string().min(1, "Contract is required"),
disciplineId: z.number().min(1, "Discipline is required"),
rfaTypeId: z.number().min(1, "Type is required"),
subject: z.string().min(5, "Subject must be at least 5 characters"),
description: z.string().optional(),
body: z.string().optional(),
remarks: z.string().optional(),
toOrganizationId: z.number().min(1, "Please select To Organization"),
toOrganizationId: z.string().min(1, "Please select To Organization"),
dueDate: z.string().optional(),
shopDrawingRevisionIds: z.array(z.number()).optional(),
items: z.array(rfaItemSchema).min(1, "At least one item is required"),
@@ -63,14 +63,14 @@ export function RFAForm() {
} = useForm<RFAFormData>({
resolver: zodResolver(rfaSchema),
defaultValues: {
contractId: 0,
contractId: "",
disciplineId: 0,
rfaTypeId: 0,
subject: "",
description: "",
body: "",
remarks: "",
toOrganizationId: 0,
toOrganizationId: "",
dueDate: "",
shopDrawingRevisionIds: [],
items: [{ itemNo: "1", description: "", quantity: 0, unit: "" }],
@@ -182,7 +182,7 @@ export function RFAForm() {
<div>
<Label>Contract *</Label>
<Select
onValueChange={(val) => setValue("contractId", Number(val))}
onValueChange={(val) => setValue("contractId", val)}
disabled={isLoadingContracts}
>
<SelectTrigger>
@@ -190,8 +190,8 @@ export function RFAForm() {
</SelectTrigger>
<SelectContent>
{contracts?.map((c: any) => (
<SelectItem key={c.id || c.contract_id} value={String(c.id || c.contract_id)}>
{c.name || c.contract_no}
<SelectItem key={c.id} value={String(c.id)}>
{c.contractName || c.name || c.contractCode}
</SelectItem>
))}
</SelectContent>
@@ -59,7 +59,7 @@ const itemSchema = z.object({
// Main form schema
const formSchema = z.object({
correspondenceId: z.number().min(1, "Correspondence is required"), // Linked correspondence (e.g. Originator Letter)
correspondenceId: z.string().min(1, "Correspondence is required"), // ADR-019: UUID string
subject: z.string().min(1, "Subject is required"),
purpose: z.enum(["FOR_APPROVAL", "FOR_INFORMATION", "FOR_REVIEW", "OTHER"]),
remarks: z.string().optional(),
@@ -127,7 +127,7 @@ export function TransmittalForm() {
const selectedDocId = form.watch("correspondenceId");
const selectedDoc = correspondences?.data?.find(
(c: { id: number }) => c.id === selectedDocId
(c: { uuid: string }) => c.uuid === selectedDocId
);
return (
@@ -172,19 +172,19 @@ export function TransmittalForm() {
<CommandEmpty>No document found.</CommandEmpty>
<CommandGroup>
{correspondences?.data?.map(
(doc: { id: number; correspondence_number: string }) => (
(doc: { uuid: string; correspondence_number: string }) => (
<CommandItem
key={doc.id}
key={doc.uuid}
value={doc.correspondence_number}
onSelect={() => {
form.setValue("correspondenceId", doc.id);
form.setValue("correspondenceId", doc.uuid);
setDocOpen(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
doc.id === field.value
doc.uuid === field.value
? "opacity-100"
: "opacity-0"
)}
+4 -3
View File
@@ -104,10 +104,11 @@ export function useCorrespondenceTypes() {
// --- Drawing Categories Hooks ---
export function useContractDrawingCategories() {
export function useContractDrawingCategories(projectId?: number | string) {
return useQuery({
queryKey: ['contract-drawing-categories'],
queryFn: () => masterDataService.getContractDrawingCategories(),
queryKey: ['contract-drawing-categories', projectId],
queryFn: () => masterDataService.getContractDrawingCategories(projectId),
enabled: !!projectId,
});
}
+3 -2
View File
@@ -10,7 +10,7 @@ import apiClient from '@/lib/api/client';
*/
export interface NumberingTemplate {
id: number;
projectId: number;
projectId: number | string;
correspondenceTypeId: number | null; // null = Default Format for project
correspondenceType?: {
id: number;
@@ -18,9 +18,10 @@ export interface NumberingTemplate {
typeName: string;
} | null;
project?: {
id: number;
id: number | string;
projectCode: string;
projectName: string;
uuid?: string;
};
formatTemplate: string;
description?: string;
+4 -2
View File
@@ -187,8 +187,10 @@ export const masterDataService = {
// --- Drawing Categories ---
getContractDrawingCategories: async () => {
const response = await apiClient.get("/drawings/contract/categories");
getContractDrawingCategories: async (projectId?: number | string) => {
const response = await apiClient.get("/drawings/contract/categories", {
params: { projectId }
});
return response.data.data || response.data;
},
@@ -15,7 +15,7 @@ const mapWorkflow = (backendObj: any): Workflow => {
workflowId: backendObj.id,
workflowName: backendObj.dsl?.workflowName || backendObj.workflow_code,
description: backendObj.description || backendObj.dsl?.description || '',
workflowType: backendObj.workflow_code,
workflowType: backendObj.workflow_code?.toUpperCase() || backendObj.workflow_code,
version: backendObj.version || 1,
isActive: backendObj.is_active,
dslDefinition: typeof backendObj.dsl === 'string' ? backendObj.dsl : backendObj.dsl?.dslDefinition || JSON.stringify(backendObj.dsl, null, 2),
+8 -5
View File
@@ -2,8 +2,8 @@
// --- Create ---
export interface CreateRfaDto {
/** ID ของโครงการ */
projectId: number;
/** ID or UUID ของโครงการ */
projectId: number | string; // ADR-019: Accept UUID
/** ประเภท RFA (เช่น DWG, MAT) */
rfaTypeId: number;
@@ -20,8 +20,11 @@ export interface CreateRfaDto {
/** หมายเหตุ */
remarks?: string;
/** Contract UUID (optional) */
contractId?: string; // ADR-019: Contract UUID
/** ส่งถึงใคร (สำหรับ Routing Step 1) */
toOrganizationId: number;
toOrganizationId: number | string; // ADR-019: Accept UUID
/** รายละเอียดเพิ่มเติม */
description?: string;
@@ -41,8 +44,8 @@ export type UpdateRfaDto = Partial<CreateRfaDto>;
// --- Search ---
export interface SearchRfaDto {
/** Filter by Project ID (optional to allow cross-project search) */
projectId?: number;
/** Filter by Project ID or UUID (optional to allow cross-project search) */
projectId?: number | string; // ADR-019: Accept UUID
/** กรองตามประเภท RFA */
rfaTypeId?: number;
@@ -9,12 +9,12 @@ export enum TransmittalPurpose {
// --- Create ---
export interface CreateTransmittalDto {
projectId?: number;
recipientOrganizationId?: number;
projectId?: number | string; // ADR-019: Accept UUID
recipientOrganizationId?: number | string; // ADR-019: Accept UUID
subject: string;
purpose?: string;
remarks?: string;
correspondenceId: number; // For now linked correspondence
correspondenceId: number | string; // ADR-019: Accept UUID
items: CreateTransmittalItemDto[];
}
@@ -30,7 +30,7 @@ export type UpdateTransmittalDto = Partial<CreateTransmittalDto>;
// --- Search ---
export interface SearchTransmittalDto {
/** บังคับระบุ Project */
projectId: number;
projectId: number | string; // ADR-019: Accept UUID
purpose?: TransmittalPurpose;
+1 -1
View File
@@ -8,7 +8,7 @@ export interface CreateUserDto {
firstName?: string;
lastName?: string;
lineId?: string;
primaryOrganizationId?: number;
primaryOrganizationId?: number | string; // ADR-019: Accept UUID
isActive?: boolean;
}
+3 -1
View File
@@ -51,7 +51,9 @@ export interface RFA {
}
export interface CreateRFADto {
projectId: number;
projectId: number | string; // ADR-019: Accept UUID
contractId?: string; // ADR-019: Contract UUID
toOrganizationId?: number | string; // ADR-019: Recipient org UUID
rfaTypeId: number;
disciplineId?: number;
subject: string;
+3 -3
View File
@@ -20,7 +20,7 @@ export interface User {
lastName: string;
isActive: boolean;
lineId?: string;
primaryOrganizationId?: number;
primaryOrganizationId?: number | string; // ADR-019: May be INT or UUID
organization?: UserOrganization;
roles?: Role[];
@@ -42,7 +42,7 @@ export interface CreateUserDto {
password?: string;
isActive: boolean;
lineId?: string;
primaryOrganizationId?: number;
primaryOrganizationId?: number | string; // ADR-019: Accept UUID
roleIds: number[];
}
@@ -53,5 +53,5 @@ export interface SearchUserDto {
limit?: number;
search?: string;
roleId?: number;
primaryOrganizationId?: number;
primaryOrganizationId?: number | string; // ADR-019: Accept UUID
}