specweave 1.0.488 → 1.0.489

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +1 -1
  2. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +13 -5
  3. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
  4. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +28 -26
  5. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
  6. package/dist/plugins/specweave-ado/lib/ado-client.d.ts +10 -0
  7. package/dist/plugins/specweave-ado/lib/ado-client.d.ts.map +1 -1
  8. package/dist/plugins/specweave-ado/lib/ado-client.js +14 -0
  9. package/dist/plugins/specweave-ado/lib/ado-client.js.map +1 -1
  10. package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts +35 -0
  11. package/dist/plugins/specweave-ado/lib/ado-pull-sync.d.ts.map +1 -0
  12. package/dist/plugins/specweave-ado/lib/ado-pull-sync.js +53 -0
  13. package/dist/plugins/specweave-ado/lib/ado-pull-sync.js.map +1 -0
  14. package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts +46 -0
  15. package/dist/plugins/specweave-ado/lib/ado-rate-limiter.d.ts.map +1 -0
  16. package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js +65 -0
  17. package/dist/plugins/specweave-ado/lib/ado-rate-limiter.js.map +1 -0
  18. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +7 -1
  19. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
  20. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
  21. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
  22. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +17 -1
  23. package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -1
  24. package/dist/plugins/specweave-ado/lib/ado-status-sync.js +51 -9
  25. package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -1
  26. package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
  27. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  28. package/dist/plugins/specweave-github/lib/github-push-sync.d.ts.map +1 -1
  29. package/dist/plugins/specweave-github/lib/github-push-sync.js +15 -3
  30. package/dist/plugins/specweave-github/lib/github-push-sync.js.map +1 -1
  31. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +31 -1
  32. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
  33. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +170 -97
  34. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
  35. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +36 -1
  36. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
  37. package/dist/plugins/specweave-jira/lib/jira-status-sync.js +185 -82
  38. package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
  39. package/dist/src/adapters/adapter-loader.d.ts.map +1 -1
  40. package/dist/src/adapters/adapter-loader.js +8 -2
  41. package/dist/src/adapters/adapter-loader.js.map +1 -1
  42. package/dist/src/adapters/codex/adapter.d.ts.map +1 -1
  43. package/dist/src/adapters/codex/adapter.js +1 -0
  44. package/dist/src/adapters/codex/adapter.js.map +1 -1
  45. package/dist/src/adapters/cursor/adapter.d.ts.map +1 -1
  46. package/dist/src/adapters/cursor/adapter.js +1 -0
  47. package/dist/src/adapters/cursor/adapter.js.map +1 -1
  48. package/dist/src/adapters/generic/adapter.d.ts +6 -3
  49. package/dist/src/adapters/generic/adapter.d.ts.map +1 -1
  50. package/dist/src/adapters/generic/adapter.js +53 -47
  51. package/dist/src/adapters/generic/adapter.js.map +1 -1
  52. package/dist/src/adapters/kimi/adapter.d.ts +21 -0
  53. package/dist/src/adapters/kimi/adapter.d.ts.map +1 -0
  54. package/dist/src/adapters/kimi/adapter.js +57 -0
  55. package/dist/src/adapters/kimi/adapter.js.map +1 -0
  56. package/dist/src/adapters/opencode/adapter.d.ts +24 -0
  57. package/dist/src/adapters/opencode/adapter.d.ts.map +1 -0
  58. package/dist/src/adapters/opencode/adapter.js +71 -0
  59. package/dist/src/adapters/opencode/adapter.js.map +1 -0
  60. package/dist/src/adapters/registry.yaml +59 -0
  61. package/dist/src/adapters/trae/adapter.d.ts +21 -0
  62. package/dist/src/adapters/trae/adapter.d.ts.map +1 -0
  63. package/dist/src/adapters/trae/adapter.js +64 -0
  64. package/dist/src/adapters/trae/adapter.js.map +1 -0
  65. package/dist/src/cli/commands/init.d.ts.map +1 -1
  66. package/dist/src/cli/commands/init.js +156 -5
  67. package/dist/src/cli/commands/init.js.map +1 -1
  68. package/dist/src/cli/commands/update-instructions.d.ts.map +1 -1
  69. package/dist/src/cli/commands/update-instructions.js +10 -0
  70. package/dist/src/cli/commands/update-instructions.js.map +1 -1
  71. package/dist/src/cli/helpers/init/index.d.ts +1 -0
  72. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  73. package/dist/src/cli/helpers/init/index.js +2 -0
  74. package/dist/src/cli/helpers/init/index.js.map +1 -1
  75. package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -1
  76. package/dist/src/cli/helpers/init/next-steps.js +52 -0
  77. package/dist/src/cli/helpers/init/next-steps.js.map +1 -1
  78. package/dist/src/cli/helpers/init/skill-creator-installer.d.ts +24 -0
  79. package/dist/src/cli/helpers/init/skill-creator-installer.d.ts.map +1 -0
  80. package/dist/src/cli/helpers/init/skill-creator-installer.js +54 -0
  81. package/dist/src/cli/helpers/init/skill-creator-installer.js.map +1 -0
  82. package/dist/src/core/ado-description-updater.d.ts +22 -0
  83. package/dist/src/core/ado-description-updater.d.ts.map +1 -0
  84. package/dist/src/core/ado-description-updater.js +46 -0
  85. package/dist/src/core/ado-description-updater.js.map +1 -0
  86. package/dist/src/core/closure-dispatcher.d.ts +96 -0
  87. package/dist/src/core/closure-dispatcher.d.ts.map +1 -0
  88. package/dist/src/core/closure-dispatcher.js +116 -0
  89. package/dist/src/core/closure-dispatcher.js.map +1 -0
  90. package/dist/src/core/config/types.d.ts +2 -0
  91. package/dist/src/core/config/types.d.ts.map +1 -1
  92. package/dist/src/core/config/types.js.map +1 -1
  93. package/dist/src/core/errors/sync-error.d.ts +12 -0
  94. package/dist/src/core/errors/sync-error.d.ts.map +1 -0
  95. package/dist/src/core/errors/sync-error.js +19 -0
  96. package/dist/src/core/errors/sync-error.js.map +1 -0
  97. package/dist/src/core/skill-gen/rule-collector.d.ts +28 -0
  98. package/dist/src/core/skill-gen/rule-collector.d.ts.map +1 -0
  99. package/dist/src/core/skill-gen/rule-collector.js +112 -0
  100. package/dist/src/core/skill-gen/rule-collector.js.map +1 -0
  101. package/dist/src/core/skill-gen/signal-collector.d.ts +2 -1
  102. package/dist/src/core/skill-gen/signal-collector.d.ts.map +1 -1
  103. package/dist/src/core/skill-gen/signal-collector.js +21 -5
  104. package/dist/src/core/skill-gen/signal-collector.js.map +1 -1
  105. package/dist/src/core/sync/persistent-circuit-breaker.d.ts +22 -0
  106. package/dist/src/core/sync/persistent-circuit-breaker.d.ts.map +1 -0
  107. package/dist/src/core/sync/persistent-circuit-breaker.js +65 -0
  108. package/dist/src/core/sync/persistent-circuit-breaker.js.map +1 -0
  109. package/dist/src/core/sync/retry-wrapper.d.ts +13 -0
  110. package/dist/src/core/sync/retry-wrapper.d.ts.map +1 -0
  111. package/dist/src/core/sync/retry-wrapper.js +37 -0
  112. package/dist/src/core/sync/retry-wrapper.js.map +1 -0
  113. package/dist/src/importers/ac-parser.d.ts +27 -0
  114. package/dist/src/importers/ac-parser.d.ts.map +1 -0
  115. package/dist/src/importers/ac-parser.js +47 -0
  116. package/dist/src/importers/ac-parser.js.map +1 -0
  117. package/dist/src/sync/types.d.ts +8 -0
  118. package/dist/src/sync/types.d.ts.map +1 -1
  119. package/dist/src/sync/types.js +12 -0
  120. package/dist/src/sync/types.js.map +1 -1
  121. package/package.json +1 -1
  122. package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +65 -0
  123. package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +83 -0
  124. package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +68 -0
  125. package/plugins/specweave/skills/skill-gen/SKILL.md +20 -3
  126. package/plugins/specweave/skills/team-lead/agents/architect.md +52 -0
  127. package/plugins/specweave/skills/team-lead/agents/pm.md +50 -0
  128. package/plugins/specweave/skills/team-lead/agents/researcher.md +64 -0
  129. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +23 -21
  130. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +37 -29
  131. package/plugins/specweave-ado/lib/ado-client.js +14 -0
  132. package/plugins/specweave-ado/lib/ado-client.ts +18 -0
  133. package/plugins/specweave-ado/lib/ado-pull-sync.js +35 -0
  134. package/plugins/specweave-ado/lib/ado-pull-sync.ts +74 -0
  135. package/plugins/specweave-ado/lib/ado-rate-limiter.js +56 -0
  136. package/plugins/specweave-ado/lib/ado-rate-limiter.ts +86 -0
  137. package/plugins/specweave-ado/lib/ado-spec-sync.js +25 -1
  138. package/plugins/specweave-ado/lib/ado-spec-sync.ts +32 -2
  139. package/plugins/specweave-ado/lib/ado-status-sync.js +52 -14
  140. package/plugins/specweave-ado/lib/ado-status-sync.ts +64 -16
  141. package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
  142. package/plugins/specweave-github/lib/github-push-sync.js +11 -3
  143. package/plugins/specweave-github/lib/github-push-sync.ts +16 -3
  144. package/plugins/specweave-jira/lib/jira-spec-sync.js +60 -1
  145. package/plugins/specweave-jira/lib/jira-spec-sync.ts +93 -1
  146. package/plugins/specweave-jira/lib/jira-status-sync.js +151 -109
  147. package/plugins/specweave-jira/lib/jira-status-sync.ts +161 -39
@@ -0,0 +1,68 @@
1
+ You are the TYPE DESIGN REVIEWER agent.
2
+
3
+ REVIEW TARGET: [REVIEW_TARGET]
4
+
5
+ MISSION:
6
+ Analyze type system quality — find overly broad types, unsafe assertions, missing
7
+ invariants, and type designs that don't leverage the compiler to prevent bugs.
8
+ Good types make illegal states unrepresentable. Your job is to find where the type
9
+ system could work harder. You are a read-only analyst — FIND issues, not fix them.
10
+
11
+ SCOPE:
12
+ - If reviewing a PR: run `gh pr diff [PR_NUMBER]` to get the diff, then analyze changed files
13
+ - If reviewing a module: read all files in the target path
14
+ - Focus on TypeScript/JavaScript files (.ts, .tsx, .js, .jsx)
15
+ - Skip type-checking config files, test fixtures, and generated code
16
+
17
+ CHECKLIST:
18
+ 1. Explicit `any` type usage — should almost always be `unknown` or a proper type
19
+ 2. Type assertions (`as Type`, `!` non-null) that bypass the type system unsafely
20
+ 3. Overly broad types (string where a union literal is appropriate, e.g., status: string vs status: "active" | "inactive")
21
+ 4. Missing discriminated unions for state machines or multi-state objects
22
+ 5. Interface vs type alias misuse (interfaces for objects, types for unions/intersections)
23
+ 6. Generic types that could be more constrained (T vs T extends SomeBase)
24
+ 7. Missing readonly modifiers on data that should be immutable
25
+ 8. Index signatures ([key: string]: any) when specific keys are known
26
+ 9. Function return types that are too wide (returns string | number | undefined when narrowable)
27
+ 10. Missing or incorrect type predicates and type guards
28
+ 11. Zod/io-ts/valibot schemas that diverge from their TypeScript type counterparts
29
+ 12. Enums used where const objects or union types would be safer and more tree-shakeable
30
+
31
+ OUTPUT FORMAT:
32
+ Produce a structured findings report using this format for each finding:
33
+
34
+ ### [SEVERITY]: [Title]
35
+ - **File**: path/to/file.ts:line
36
+ - **Category**: Type issue (e.g., Unsafe assertion, Overly broad type, Missing discriminant)
37
+ - **Description**: What the type issue is and what it allows that shouldn't be possible
38
+ - **Impact**: What bugs this enables (runtime type errors, invalid state, refactoring hazard)
39
+ - **Recommendation**: The better type design with code example
40
+ - **Code snippet**: The current type (keep brief)
41
+
42
+ Severity levels: CRITICAL | HIGH | MEDIUM | LOW | INFO
43
+
44
+ COMMUNICATION:
45
+ When done, signal completion:
46
+ SendMessage({
47
+ type: "message",
48
+ recipient: "team-lead",
49
+ content: "REVIEW_COMPLETE: Type design review finished. Found [N] issues: [X critical, Y high, Z medium]. Key findings: [brief summary of top 3].",
50
+ summary: "Type design review complete"
51
+ })
52
+
53
+ If you need clarification about type conventions:
54
+ SendMessage({
55
+ type: "message",
56
+ recipient: "team-lead",
57
+ content: "REVIEW_QUESTION: [your question]",
58
+ summary: "Type reviewer needs clarification"
59
+ })
60
+
61
+ RULES:
62
+ - READ-ONLY: Do not modify any files
63
+ - Be specific: include file paths and line numbers for every finding
64
+ - Prioritize: CRITICAL and HIGH findings first
65
+ - No speculation: only report issues with concrete reasoning about what goes wrong
66
+ - Consider project style: if the project consistently uses a pattern, note it but don't fight it
67
+ - Skip generated code: don't flag types in auto-generated files (prisma client, graphql codegen)
68
+ - TypeScript/JavaScript only: skip non-TS files entirely
@@ -52,13 +52,18 @@ Wait for the user to select a pattern by name or number. The user responds in na
52
52
 
53
53
  ### Step 4: Check Skill-Creator Plugin
54
54
 
55
- Verify Anthropic's official skill-creator is available:
55
+ Verify Anthropic's official skill-creator is available (local-first, then global fallback):
56
56
 
57
57
  ```bash
58
- SKILL_CREATOR_PATH=$(find ~/.claude/plugins/cache/claude-plugins-official/skill-creator -name "SKILL.md" -maxdepth 3 2>/dev/null | head -1)
58
+ # Check local project copy first (auto-installed by specweave init)
59
+ SKILL_CREATOR_PATH=".claude/skills/skill-creator/SKILL.md"
60
+ if [ ! -f "$SKILL_CREATOR_PATH" ]; then
61
+ # Fall back to global plugin cache
62
+ SKILL_CREATOR_PATH=$(find ~/.claude/plugins/cache/claude-plugins-official/skill-creator -name "SKILL.md" -maxdepth 3 2>/dev/null | head -1)
63
+ fi
59
64
  if [ -z "$SKILL_CREATOR_PATH" ]; then
60
65
  echo "ERROR: Anthropic's skill-creator plugin is not installed."
61
- echo "Install it via: claude plugin install skill-creator"
66
+ echo "Install it via: claude install-skill https://github.com/anthropics/claude-code/tree/main/skill-creator"
62
67
  echo ""
63
68
  echo "The skill-creator is required to build tested, benchmarked skills."
64
69
  exit 1
@@ -67,6 +72,18 @@ fi
67
72
 
68
73
  ### Step 5: Delegate to Skill-Creator
69
74
 
75
+ **Slug dedup guard** — before delegating, check if a skill with this slug already exists:
76
+
77
+ ```bash
78
+ SKILL_SLUG="$SELECTED_PATTERN_SLUG" # e.g. "error-handling"
79
+ SKILL_DIR=".claude/skills/$SKILL_SLUG"
80
+ if [ -d "$SKILL_DIR" ] && [ -f "$SKILL_DIR/SKILL.md" ]; then
81
+ echo "Skill '$SKILL_SLUG' already exists at $SKILL_DIR/SKILL.md -- skipping generation."
82
+ # Mark signal as generated in skill-signals.json and continue to next pattern
83
+ exit 0
84
+ fi
85
+ ```
86
+
70
87
  Invoke the skill-creator with the selected pattern context:
71
88
 
72
89
  1. **Provide context** to skill-creator:
@@ -0,0 +1,52 @@
1
+ You are the ARCHITECT PLANNING agent for increment [INCREMENT_ID].
2
+
3
+ MASTER SPEC (SOURCE OF TRUTH):
4
+ The feature is specified in [MASTER_INCREMENT_PATH]/spec.md.
5
+ Read the spec BEFORE designing anything. Your architecture MUST satisfy all ACs.
6
+
7
+ MISSION:
8
+ Produce plan.md with system architecture, component design, and ADRs for key decisions.
9
+ You own the HOW — defining the technical approach. You work in parallel with
10
+ the Security reviewer who validates your design for vulnerabilities.
11
+
12
+ SKILLS TO INVOKE:
13
+ Skill({ skill: "sw:architect" })
14
+
15
+ FILE OWNERSHIP (WRITE access):
16
+ [MASTER_INCREMENT_PATH]/plan.md
17
+ .specweave/docs/internal/architecture/adr/ (new ADRs only)
18
+
19
+ READ ACCESS: Any file in the repository
20
+
21
+ UPSTREAM DEPENDENCY:
22
+ Wait for the PM agent to signal PLAN_READY or COMPLETION before starting.
23
+ You need spec.md to exist with user stories and ACs before you can design.
24
+
25
+ WORKFLOW:
26
+ 1. Read spec.md at [MASTER_INCREMENT_PATH]/spec.md
27
+ 2. Explore the codebase to understand existing architecture, patterns, and tech stack
28
+ 3. Check existing ADRs at .specweave/docs/internal/architecture/adr/
29
+ 4. Design system architecture:
30
+ - Component boundaries and responsibilities
31
+ - Data flow and state management
32
+ - API contracts and integration points
33
+ - Error handling strategy
34
+ - Performance considerations
35
+ 5. Write ADRs for significant architectural decisions (use ADR template format)
36
+ 6. Write plan.md to [MASTER_INCREMENT_PATH]/plan.md
37
+ 7. Signal architecture decisions:
38
+ SendMessage({ type: "message", recipient: "team-lead",
39
+ content: "CONTRACT_READY: Architecture defined in plan.md.\nComponents: [list]\nKey patterns: [e.g., CQRS, event-driven]\nADRs created: [list or 'none']\nTech stack: [decisions]",
40
+ summary: "Architect: plan.md ready with architecture" })
41
+ 8. Signal COMPLETION:
42
+ SendMessage({ type: "message", recipient: "team-lead",
43
+ content: "COMPLETION: plan.md finalized.\nComponents: [count]\nADRs: [count]\nKey risk: [biggest concern]",
44
+ summary: "Architect agent: plan complete" })
45
+
46
+ RULES:
47
+ - WRITE only plan.md and ADRs — do not modify spec.md or create tasks.md
48
+ - Every architectural decision must be justified (not just "use X because it's popular")
49
+ - Consider scalability, maintainability, testability, and security
50
+ - Reference existing codebase patterns — don't propose patterns alien to the project
51
+ - Flag technical risks and mitigation strategies
52
+ - Keep plan.md actionable — an implementer should be able to code from it
@@ -0,0 +1,50 @@
1
+ You are the PM PLANNING agent for increment [INCREMENT_ID].
2
+
3
+ FEATURE DESCRIPTION: [FEATURE_DESCRIPTION]
4
+
5
+ MASTER INCREMENT PATH: [MASTER_INCREMENT_PATH]
6
+
7
+ MISSION:
8
+ Produce a comprehensive spec.md with user stories, acceptance criteria, and scope
9
+ boundaries. You own the WHAT — defining what the feature does and how success is
10
+ measured. You work in parallel with the Architect agent who owns the HOW.
11
+
12
+ SKILLS TO INVOKE:
13
+ Skill({ skill: "sw:pm" })
14
+
15
+ FILE OWNERSHIP (WRITE access):
16
+ [MASTER_INCREMENT_PATH]/spec.md
17
+
18
+ READ ACCESS: Any file in the repository (for understanding existing patterns and domain)
19
+
20
+ WORKFLOW:
21
+ 1. Read the feature description and any existing context
22
+ 2. Explore the codebase to understand the domain, existing patterns, and constraints
23
+ 3. Identify stakeholders, personas, and key use cases
24
+ 4. Write user stories with acceptance criteria following the format:
25
+ ### US-NNN: Story Title
26
+ **Project**: [project-name]
27
+ **As a** [role]
28
+ **I want** [capability]
29
+ **So that** [benefit]
30
+ **Acceptance Criteria**:
31
+ - [ ] **AC-USNN-01**: [Criterion]
32
+ 5. Define scope boundaries (in-scope vs out-of-scope)
33
+ 6. Write spec.md to [MASTER_INCREMENT_PATH]/spec.md
34
+ 7. Send PLAN_READY notification (do NOT wait for response):
35
+ SendMessage({ type: "message", recipient: "team-lead",
36
+ content: "PLAN_READY: spec.md written at [MASTER_INCREMENT_PATH]/spec.md\nUser Stories: [count]\nACs: [count]\nKey decisions: [1-2 sentence summary]",
37
+ summary: "PM: spec.md ready — proceeding" })
38
+ 8. Proceed immediately. If team-lead sends PLAN_CORRECTION, revise spec.md accordingly.
39
+ 9. Signal COMPLETION:
40
+ SendMessage({ type: "message", recipient: "team-lead",
41
+ content: "COMPLETION: spec.md finalized.\nStories: [count]\nACs: [count]\nScope: [brief summary]",
42
+ summary: "PM agent: spec complete" })
43
+
44
+ RULES:
45
+ - WRITE only spec.md — do not create plan.md or tasks.md (Architect and Planner own those)
46
+ - Every user story MUST have a **Project**: field
47
+ - Every AC MUST use the AC-USNN-NN format for bidirectional linking
48
+ - Be specific in ACs — testable, not vague ("user can log in" not "auth works")
49
+ - Consider edge cases, error states, and non-functional requirements
50
+ - Do NOT scope-creep — stick to the feature description
@@ -0,0 +1,64 @@
1
+ You are the RESEARCHER agent.
2
+
3
+ RESEARCH TOPIC: [RESEARCH_TOPIC]
4
+ RESEARCH SCOPE: [RESEARCH_SCOPE]
5
+
6
+ MISSION:
7
+ Investigate the given topic thoroughly — explore the codebase, search the web,
8
+ analyze patterns, and compile actionable findings. You are a read-only analyst.
9
+ Your job is to FIND information, not implement changes.
10
+
11
+ APPROACH:
12
+ 1. Parse the research scope to understand what information is needed
13
+ 2. Explore the codebase for relevant patterns, implementations, and conventions
14
+ 3. Search the web for related technologies, best practices, and alternatives
15
+ 4. Cross-reference findings — validate web claims against actual codebase state
16
+ 5. Compile a structured research report
17
+
18
+ YOUR REPORT MUST INCLUDE:
19
+
20
+ ### Executive Summary
21
+ 2-3 sentence overview of key findings and recommendation.
22
+
23
+ ### Current State
24
+ What exists today in the codebase. Include file paths and line references.
25
+
26
+ ### External Research
27
+ What the broader ecosystem offers. Technologies, libraries, patterns considered.
28
+ Include sources and links where relevant.
29
+
30
+ ### Analysis
31
+ Compare options. Use a decision matrix if comparing 3+ alternatives:
32
+ | Option | Pros | Cons | Effort | Fit |
33
+
34
+ ### Recommendations
35
+ Concrete, actionable recommendations ranked by priority.
36
+ Each should include: what to do, why, and estimated effort.
37
+
38
+ ### Open Questions
39
+ Things that need further investigation or user input.
40
+
41
+ COMMUNICATION:
42
+ When done, signal completion:
43
+ SendMessage({
44
+ type: "message",
45
+ recipient: "team-lead",
46
+ content: "RESEARCH_COMPLETE: [topic] research finished.\nKey finding: [most important insight]\nRecommendation: [primary recommendation]\nOpen questions: [count]",
47
+ summary: "Research complete: [topic]"
48
+ })
49
+
50
+ For significant discoveries during research:
51
+ SendMessage({
52
+ type: "message",
53
+ recipient: "team-lead",
54
+ content: "INSIGHT: [important discovery that may affect scope or approach]",
55
+ summary: "Researcher found insight"
56
+ })
57
+
58
+ RULES:
59
+ - READ-ONLY: Do not modify any files
60
+ - Be thorough: explore multiple angles, not just the first result
61
+ - Be specific: include file paths, line numbers, URLs — not vague references
62
+ - Be honest: flag uncertainty and gaps in knowledge
63
+ - Stay scoped: answer the research question, don't expand into tangential topics
64
+ - Cite sources: for web findings, include the source URL or reference
@@ -5,6 +5,7 @@ import axios from "axios";
5
5
  import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
6
6
  import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
7
7
  import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
8
+ import { AdoDescriptionUpdater } from "../../../src/core/ado-description-updater.js";
8
9
  class AdoACCheckboxSync {
9
10
  constructor(options) {
10
11
  this.projectRoot = options.projectRoot;
@@ -88,47 +89,48 @@ class AdoACCheckboxSync {
88
89
  }
89
90
  }
90
91
  /**
91
- * Fetch current HTML description, regex-flip ☐/☑ for matching ACs, PATCH back.
92
- * Posts a comment listing all AC statuses when any AC changed.
92
+ * Update a work item's AC section using API-based section manipulation.
93
+ *
94
+ * GET description → formatACCheckboxes → updateAcSection → PATCH with
95
+ * JSON Patch op:"replace". No regex used at any point.
93
96
  */
94
- async updateStoryCheckboxes(client, adoConfig, storyId, acStatus) {
97
+ async updateWorkItemACSection(client, storyId, acStatus) {
98
+ const updater = new AdoDescriptionUpdater();
95
99
  const resp = await client.get(
96
100
  `/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
97
101
  );
98
- let description = resp.data?.fields?.["System.Description"] ?? "";
99
- const original = description;
100
- let updatedCount = 0;
101
- for (const [acId, completed] of acStatus) {
102
- const escaped = acId.replace(/-/g, "\\-");
103
- const beforeRegex = new RegExp(`(\u2610|\u2611)\\s+(${escaped}:)`, "g");
104
- const newCheckbox = completed ? "\u2611" : "\u2610";
105
- const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
106
- if (replaced !== description) {
107
- updatedCount++;
108
- description = replaced;
109
- }
110
- }
111
- if (description === original) return 0;
102
+ const currentDescription = resp.data?.fields?.["System.Description"] ?? "";
103
+ const newAcHtml = updater.formatACCheckboxes(acStatus);
104
+ if (!newAcHtml) return 0;
105
+ const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
106
+ if (updatedDescription === currentDescription) return 0;
112
107
  await client.patch(
113
108
  `/wit/workitems/${storyId}?api-version=7.0`,
114
- [{ op: "replace", path: "/fields/System.Description", value: description }],
109
+ [{ op: "replace", path: "/fields/System.Description", value: updatedDescription }],
115
110
  { headers: { "Content-Type": "application/json-patch+json" } }
116
111
  );
117
112
  const completedCount = [...acStatus.values()].filter(Boolean).length;
118
113
  const totalCount = acStatus.size;
119
114
  const percentage = Math.round(completedCount / totalCount * 100);
120
115
  const acLines = [...acStatus.entries()].map(([id, done]) => `<li>${done ? "\u2705" : "\u2B1C"} ${id}</li>`).join("\n");
121
- const commentHtml = `<h3>\u{1F4CA} AC Progress Update</h3>
116
+ const commentHtml = `<h3>AC Progress Update</h3>
122
117
  <p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
123
118
  <ul>
124
119
  ${acLines}
125
120
  </ul>
126
- <p>\u{1F916} Auto-updated by SpecWeave AC Completion Gate</p>`;
121
+ <p>Auto-updated by SpecWeave</p>`;
127
122
  await client.post(
128
123
  `/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
129
124
  { text: commentHtml }
130
125
  );
131
- return updatedCount;
126
+ return acStatus.size;
127
+ }
128
+ /**
129
+ * Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
130
+ * Kept for backward compatibility. New code should use updateWorkItemACSection.
131
+ */
132
+ async updateStoryCheckboxes(client, adoConfig, storyId, acStatus) {
133
+ return this.updateWorkItemACSection(client, storyId, acStatus);
132
134
  }
133
135
  buildClient(adoConfig) {
134
136
  const token = Buffer.from(`:${adoConfig.pat}`).toString("base64");
@@ -2,10 +2,10 @@
2
2
  * ADO AC Checkbox Sync
3
3
  *
4
4
  * When ACs are marked complete in spec.md, updates the HTML description
5
- * of the linked ADO work item (☐ ☑) and posts a progress comment.
5
+ * of the linked ADO work item using API-based section update (not regex).
6
6
  *
7
- * Mirrors jira-ac-checkbox-sync.ts pattern for ADO.
8
- * The ADO description is HTML — we use regex to flip ☐/☑ by AC ID.
7
+ * Uses AdoDescriptionUpdater for clean HTML section manipulation via
8
+ * sentinel comments (<!-- AC_SECTION_START/END -->).
9
9
  */
10
10
 
11
11
  import { promises as fs, existsSync } from 'fs';
@@ -15,6 +15,7 @@ import axios, { AxiosInstance } from 'axios';
15
15
  import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
16
16
  import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
17
17
  import { GitHubACCheckboxSync } from '../../specweave-github/lib/github-ac-checkbox-sync.js';
18
+ import { AdoDescriptionUpdater } from '../../../src/core/ado-description-updater.js';
18
19
  import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
19
20
  import type { LivingDocsUSFile } from '../../../src/types/living-docs-us-file.js';
20
21
 
@@ -131,41 +132,35 @@ export class AdoACCheckboxSync {
131
132
  }
132
133
 
133
134
  /**
134
- * Fetch current HTML description, regex-flip ☐/☑ for matching ACs, PATCH back.
135
- * Posts a comment listing all AC statuses when any AC changed.
135
+ * Update a work item's AC section using API-based section manipulation.
136
+ *
137
+ * GET description → formatACCheckboxes → updateAcSection → PATCH with
138
+ * JSON Patch op:"replace". No regex used at any point.
136
139
  */
137
- private async updateStoryCheckboxes(
140
+ async updateWorkItemACSection(
138
141
  client: AxiosInstance,
139
- adoConfig: { organization: string; project: string; pat: string },
140
142
  storyId: number,
141
- acStatus: Map<string, boolean>
143
+ acStatus: Map<string, boolean>,
142
144
  ): Promise<number> {
145
+ const updater = new AdoDescriptionUpdater();
146
+
143
147
  const resp = await client.get(
144
148
  `/wit/workitems/${storyId}?fields=System.Description&api-version=7.0`
145
149
  );
146
150
 
147
- let description: string = resp.data?.fields?.['System.Description'] ?? '';
148
- const original = description;
149
- let updatedCount = 0;
150
-
151
- for (const [acId, completed] of acStatus) {
152
- const escaped = acId.replace(/-/g, '\\-');
153
- // Pattern: ☐ AC-US1-01: or ☑ AC-US1-01:
154
- const beforeRegex = new RegExp(`(☐|☑)\\s+(${escaped}:)`, 'g');
155
- const newCheckbox = completed ? '☑' : '☐';
156
- const replaced = description.replace(beforeRegex, `${newCheckbox} $2`);
157
- if (replaced !== description) {
158
- updatedCount++;
159
- description = replaced;
160
- }
161
- }
151
+ const currentDescription: string = resp.data?.fields?.['System.Description'] ?? '';
152
+ const newAcHtml = updater.formatACCheckboxes(acStatus);
153
+
154
+ if (!newAcHtml) return 0;
162
155
 
163
- if (description === original) return 0;
156
+ const updatedDescription = updater.updateAcSection(currentDescription, newAcHtml);
164
157
 
165
- // PATCH description
158
+ if (updatedDescription === currentDescription) return 0;
159
+
160
+ // PATCH description via JSON Patch
166
161
  await client.patch(
167
162
  `/wit/workitems/${storyId}?api-version=7.0`,
168
- [{ op: 'replace', path: '/fields/System.Description', value: description }],
163
+ [{ op: 'replace', path: '/fields/System.Description', value: updatedDescription }],
169
164
  { headers: { 'Content-Type': 'application/json-patch+json' } }
170
165
  );
171
166
 
@@ -178,19 +173,32 @@ export class AdoACCheckboxSync {
178
173
  .map(([id, done]) => `<li>${done ? '✅' : '⬜'} ${id}</li>`)
179
174
  .join('\n');
180
175
 
181
- const commentHtml = `<h3>📊 AC Progress Update</h3>
176
+ const commentHtml = `<h3>AC Progress Update</h3>
182
177
  <p><strong>Acceptance Criteria</strong>: ${completedCount}/${totalCount} (${percentage}%)</p>
183
178
  <ul>
184
179
  ${acLines}
185
180
  </ul>
186
- <p>🤖 Auto-updated by SpecWeave AC Completion Gate</p>`;
181
+ <p>Auto-updated by SpecWeave</p>`;
187
182
 
188
183
  await client.post(
189
184
  `/wit/workItems/${storyId}/comments?api-version=7.1-preview.3`,
190
185
  { text: commentHtml }
191
186
  );
192
187
 
193
- return updatedCount;
188
+ return acStatus.size;
189
+ }
190
+
191
+ /**
192
+ * Legacy: Fetch current HTML description, regex-flip checkboxes, PATCH back.
193
+ * Kept for backward compatibility. New code should use updateWorkItemACSection.
194
+ */
195
+ private async updateStoryCheckboxes(
196
+ client: AxiosInstance,
197
+ adoConfig: { organization: string; project: string; pat: string },
198
+ storyId: number,
199
+ acStatus: Map<string, boolean>
200
+ ): Promise<number> {
201
+ return this.updateWorkItemACSection(client, storyId, acStatus);
194
202
  }
195
203
 
196
204
  private buildClient(adoConfig: { organization: string; project: string; pat: string }): AxiosInstance {
@@ -114,6 +114,20 @@ class AdoClient {
114
114
  const url = `${this.baseUrl}/_apis/wit/workitems/${id}?api-version=7.1`;
115
115
  await this.request("DELETE", url);
116
116
  }
117
+ /**
118
+ * Pull the current state of a work item for bidirectional sync.
119
+ *
120
+ * @param id - ADO work item ID
121
+ * @returns state and last-modified timestamp
122
+ */
123
+ async pullWorkItemState(id) {
124
+ const url = `${this.baseUrl}/_apis/wit/workitems/${id}?$select=System.State,System.ChangedDate&api-version=7.1`;
125
+ const item = await this.request("GET", url);
126
+ return {
127
+ state: item.fields["System.State"],
128
+ modifiedAt: new Date(item.fields["System.ChangedDate"])
129
+ };
130
+ }
117
131
  // ==========================================================================
118
132
  // Comment Operations
119
133
  // ==========================================================================
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import https from 'https';
11
+ import { AdoRateLimiter } from './ado-rate-limiter.js';
11
12
 
12
13
  // ============================================================================
13
14
  // Types
@@ -20,6 +21,8 @@ export interface AdoConfig {
20
21
  workItemType?: 'Epic' | 'Feature' | 'User Story';
21
22
  areaPath?: string;
22
23
  iterationPath?: string;
24
+ /** Optional rate limiter instance (shared across client instances) */
25
+ rateLimiter?: AdoRateLimiter;
23
26
  }
24
27
 
25
28
  export interface WorkItem {
@@ -207,6 +210,21 @@ export class AdoClient {
207
210
  await this.request('DELETE', url);
208
211
  }
209
212
 
213
+ /**
214
+ * Pull the current state of a work item for bidirectional sync.
215
+ *
216
+ * @param id - ADO work item ID
217
+ * @returns state and last-modified timestamp
218
+ */
219
+ async pullWorkItemState(id: number): Promise<{ state: string; modifiedAt: Date }> {
220
+ const url = `${this.baseUrl}/_apis/wit/workitems/${id}?$select=System.State,System.ChangedDate&api-version=7.1`;
221
+ const item = await this.request<WorkItem>('GET', url);
222
+ return {
223
+ state: item.fields['System.State'],
224
+ modifiedAt: new Date(item.fields['System.ChangedDate']),
225
+ };
226
+ }
227
+
210
228
  // ==========================================================================
211
229
  // Comment Operations
212
230
  // ==========================================================================
@@ -0,0 +1,35 @@
1
+ function mapAdoStateToSpecweave(adoState) {
2
+ const map = {
3
+ "New": "planned",
4
+ "To Do": "planned",
5
+ "Active": "active",
6
+ "Doing": "active",
7
+ "In Progress": "active",
8
+ "Resolved": "completed",
9
+ "Closed": "completed",
10
+ "Done": "completed",
11
+ "Removed": "abandoned"
12
+ };
13
+ return map[adoState] ?? "active";
14
+ }
15
+ function pullAdoChanges(ctx) {
16
+ if (!ctx.canUpsertInternalItems) {
17
+ return { changed: false, reason: "canUpsertInternalItems is false" };
18
+ }
19
+ const mappedStatus = mapAdoStateToSpecweave(ctx.adoState);
20
+ if (mappedStatus === ctx.localStatus) {
21
+ return { changed: false, reason: "states are equivalent" };
22
+ }
23
+ if (ctx.adoModifiedAt.getTime() <= ctx.localUpdatedAt.getTime()) {
24
+ return { changed: false, reason: "local is newer" };
25
+ }
26
+ return {
27
+ changed: true,
28
+ newStatus: mappedStatus,
29
+ reason: `ADO state "${ctx.adoState}" is newer \u2014 updating to "${mappedStatus}"`
30
+ };
31
+ }
32
+ export {
33
+ mapAdoStateToSpecweave,
34
+ pullAdoChanges
35
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ADO Pull Sync — Bidirectional state synchronization
3
+ *
4
+ * Implements last-write-wins conflict resolution for pulling
5
+ * ADO work item state changes back into SpecWeave metadata.
6
+ *
7
+ * @module ado-pull-sync
8
+ */
9
+
10
+ export interface PullSyncContext {
11
+ localStatus: string;
12
+ localUpdatedAt: Date;
13
+ adoState: string;
14
+ adoModifiedAt: Date;
15
+ workItemId: number;
16
+ canUpsertInternalItems: boolean;
17
+ }
18
+
19
+ export interface PullSyncResult {
20
+ changed: boolean;
21
+ newStatus?: string;
22
+ reason?: string;
23
+ }
24
+
25
+ /**
26
+ * Map ADO work item states to SpecWeave statuses.
27
+ *
28
+ * Covers Agile/Scrum/Basic process templates.
29
+ */
30
+ export function mapAdoStateToSpecweave(adoState: string): string {
31
+ const map: Record<string, string> = {
32
+ 'New': 'planned',
33
+ 'To Do': 'planned',
34
+ 'Active': 'active',
35
+ 'Doing': 'active',
36
+ 'In Progress': 'active',
37
+ 'Resolved': 'completed',
38
+ 'Closed': 'completed',
39
+ 'Done': 'completed',
40
+ 'Removed': 'abandoned',
41
+ };
42
+
43
+ return map[adoState] ?? 'active';
44
+ }
45
+
46
+ /**
47
+ * Determine if ADO changes should override local state.
48
+ *
49
+ * Uses last-write-wins: if ADO was modified more recently than local,
50
+ * and the mapped state differs, update local metadata.
51
+ */
52
+ export function pullAdoChanges(ctx: PullSyncContext): PullSyncResult {
53
+ if (!ctx.canUpsertInternalItems) {
54
+ return { changed: false, reason: 'canUpsertInternalItems is false' };
55
+ }
56
+
57
+ const mappedStatus = mapAdoStateToSpecweave(ctx.adoState);
58
+
59
+ // Same effective status — no change needed
60
+ if (mappedStatus === ctx.localStatus) {
61
+ return { changed: false, reason: 'states are equivalent' };
62
+ }
63
+
64
+ // Last-write-wins: ADO must be newer
65
+ if (ctx.adoModifiedAt.getTime() <= ctx.localUpdatedAt.getTime()) {
66
+ return { changed: false, reason: 'local is newer' };
67
+ }
68
+
69
+ return {
70
+ changed: true,
71
+ newStatus: mappedStatus,
72
+ reason: `ADO state "${ctx.adoState}" is newer — updating to "${mappedStatus}"`,
73
+ };
74
+ }