@specship/specship-win32-arm64 0.21.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/lib/dist/activation/search-intercept.d.ts +59 -0
  2. package/lib/dist/activation/search-intercept.d.ts.map +1 -0
  3. package/lib/dist/activation/search-intercept.js +195 -0
  4. package/lib/dist/activation/search-intercept.js.map +1 -0
  5. package/lib/dist/bin/specship.js +372 -11
  6. package/lib/dist/bin/specship.js.map +1 -1
  7. package/lib/dist/bin/uninstall.d.ts +4 -3
  8. package/lib/dist/bin/uninstall.d.ts.map +1 -1
  9. package/lib/dist/bin/uninstall.js +4 -3
  10. package/lib/dist/bin/uninstall.js.map +1 -1
  11. package/lib/dist/commands/specship/jira.md +74 -0
  12. package/lib/dist/commands/specship/spec.md +61 -1
  13. package/lib/dist/extraction/index.d.ts +6 -0
  14. package/lib/dist/extraction/index.d.ts.map +1 -1
  15. package/lib/dist/extraction/index.js.map +1 -1
  16. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
  17. package/lib/dist/extraction/specs/markdown-spec-extractor.js +8 -1
  18. package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
  19. package/lib/dist/index.d.ts +7 -0
  20. package/lib/dist/index.d.ts.map +1 -1
  21. package/lib/dist/index.js +45 -18
  22. package/lib/dist/index.js.map +1 -1
  23. package/lib/dist/installer/index.d.ts +20 -6
  24. package/lib/dist/installer/index.d.ts.map +1 -1
  25. package/lib/dist/installer/index.js +123 -63
  26. package/lib/dist/installer/index.js.map +1 -1
  27. package/lib/dist/installer/targets/claude.d.ts +9 -0
  28. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  29. package/lib/dist/installer/targets/claude.js +46 -0
  30. package/lib/dist/installer/targets/claude.js.map +1 -1
  31. package/lib/dist/installer/targets/gemini.d.ts +78 -0
  32. package/lib/dist/installer/targets/gemini.d.ts.map +1 -0
  33. package/lib/dist/installer/targets/gemini.js +200 -0
  34. package/lib/dist/installer/targets/gemini.js.map +1 -0
  35. package/lib/dist/installer/targets/registry.d.ts +5 -2
  36. package/lib/dist/installer/targets/registry.d.ts.map +1 -1
  37. package/lib/dist/installer/targets/registry.js +7 -3
  38. package/lib/dist/installer/targets/registry.js.map +1 -1
  39. package/lib/dist/installer/targets/types.d.ts +6 -2
  40. package/lib/dist/installer/targets/types.d.ts.map +1 -1
  41. package/lib/dist/jira/auto-publish.d.ts +67 -0
  42. package/lib/dist/jira/auto-publish.d.ts.map +1 -0
  43. package/lib/dist/jira/auto-publish.js +256 -0
  44. package/lib/dist/jira/auto-publish.js.map +1 -0
  45. package/lib/dist/jira/board-first.d.ts +64 -0
  46. package/lib/dist/jira/board-first.d.ts.map +1 -0
  47. package/lib/dist/jira/board-first.js +91 -0
  48. package/lib/dist/jira/board-first.js.map +1 -0
  49. package/lib/dist/jira/client.d.ts +87 -1
  50. package/lib/dist/jira/client.d.ts.map +1 -1
  51. package/lib/dist/jira/client.js +249 -2
  52. package/lib/dist/jira/client.js.map +1 -1
  53. package/lib/dist/jira/config.d.ts +1 -1
  54. package/lib/dist/jira/config.d.ts.map +1 -1
  55. package/lib/dist/jira/config.js +14 -3
  56. package/lib/dist/jira/config.js.map +1 -1
  57. package/lib/dist/jira/coverage.d.ts +105 -0
  58. package/lib/dist/jira/coverage.d.ts.map +1 -0
  59. package/lib/dist/jira/coverage.js +170 -0
  60. package/lib/dist/jira/coverage.js.map +1 -0
  61. package/lib/dist/jira/index.d.ts +1 -0
  62. package/lib/dist/jira/index.d.ts.map +1 -1
  63. package/lib/dist/jira/index.js +1 -0
  64. package/lib/dist/jira/index.js.map +1 -1
  65. package/lib/dist/jira/milestone-comment.d.ts +95 -0
  66. package/lib/dist/jira/milestone-comment.d.ts.map +1 -0
  67. package/lib/dist/jira/milestone-comment.js +271 -0
  68. package/lib/dist/jira/milestone-comment.js.map +1 -0
  69. package/lib/dist/jira/publish.d.ts +86 -2
  70. package/lib/dist/jira/publish.d.ts.map +1 -1
  71. package/lib/dist/jira/publish.js +0 -0
  72. package/lib/dist/jira/publish.js.map +1 -1
  73. package/lib/dist/jira/published-specs.d.ts +29 -0
  74. package/lib/dist/jira/published-specs.d.ts.map +1 -0
  75. package/lib/dist/jira/published-specs.js +147 -0
  76. package/lib/dist/jira/published-specs.js.map +1 -0
  77. package/lib/dist/jira/reconcile.d.ts +81 -0
  78. package/lib/dist/jira/reconcile.d.ts.map +1 -0
  79. package/lib/dist/jira/reconcile.js +111 -0
  80. package/lib/dist/jira/reconcile.js.map +1 -0
  81. package/lib/dist/jira/regression-pack.d.ts +547 -0
  82. package/lib/dist/jira/regression-pack.d.ts.map +1 -0
  83. package/lib/dist/jira/regression-pack.js +986 -0
  84. package/lib/dist/jira/regression-pack.js.map +1 -0
  85. package/lib/dist/jira/repo-config.d.ts +73 -0
  86. package/lib/dist/jira/repo-config.d.ts.map +1 -0
  87. package/lib/dist/jira/repo-config.js +249 -0
  88. package/lib/dist/jira/repo-config.js.map +1 -0
  89. package/lib/dist/jira/spec-amend.d.ts +46 -0
  90. package/lib/dist/jira/spec-amend.d.ts.map +1 -0
  91. package/lib/dist/jira/spec-amend.js +232 -0
  92. package/lib/dist/jira/spec-amend.js.map +1 -0
  93. package/lib/dist/jira/spec-writer.d.ts +22 -0
  94. package/lib/dist/jira/spec-writer.d.ts.map +1 -1
  95. package/lib/dist/jira/spec-writer.js +113 -0
  96. package/lib/dist/jira/spec-writer.js.map +1 -1
  97. package/lib/dist/jira/types.d.ts +62 -0
  98. package/lib/dist/jira/types.d.ts.map +1 -1
  99. package/lib/dist/jira/types.js +15 -1
  100. package/lib/dist/jira/types.js.map +1 -1
  101. package/lib/dist/mcp/jira-tools.d.ts +102 -0
  102. package/lib/dist/mcp/jira-tools.d.ts.map +1 -1
  103. package/lib/dist/mcp/jira-tools.js +826 -53
  104. package/lib/dist/mcp/jira-tools.js.map +1 -1
  105. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  106. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  107. package/lib/dist/mcp/server-instructions.js +4 -0
  108. package/lib/dist/mcp/server-instructions.js.map +1 -1
  109. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  110. package/lib/dist/mcp/spec-tools.js +7 -1
  111. package/lib/dist/mcp/spec-tools.js.map +1 -1
  112. package/lib/dist/mcp/tools.d.ts +53 -0
  113. package/lib/dist/mcp/tools.d.ts.map +1 -1
  114. package/lib/dist/mcp/tools.js +255 -8
  115. package/lib/dist/mcp/tools.js.map +1 -1
  116. package/lib/dist/resolution/spec-link-resolver.d.ts +2 -0
  117. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  118. package/lib/dist/resolution/spec-link-resolver.js +11 -3
  119. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  120. package/lib/node_modules/.package-lock.json +1 -1
  121. package/lib/package.json +1 -1
  122. package/package.json +1 -1
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Registry of supported agent targets.
3
3
  *
4
- * Currently Claude-only. The interface is preserved so re-adding another
5
- * agent is one new file in `targets/` plus one entry below.
4
+ * Claude Code plus the one ratified exception, Gemini CLI (GEMINI-TARGET-DOC).
5
+ * Registration does NOT make a target install by default — `specship install`
6
+ * writes Claude alone unless `--target` names another (REQ-GEMINI-002.A4).
7
+ * Being here means the target participates in the contract suite, in
8
+ * `detectAll`, and in the uninstall sweep.
6
9
  */
7
10
  import { AgentTarget, Location, TargetId } from './types';
8
11
  export declare const ALL_TARGETS: readonly AgentTarget[];
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/installer/targets/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG1D,eAAO,MAAM,WAAW,EAAE,SAAS,WAAW,EAAkC,CAAC;AAEjF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAE7D;AAED,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC9C,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC9C,CAAC,CAKD"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/installer/targets/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI1D,eAAO,MAAM,WAAW,EAAE,SAAS,WAAW,EAAgD,CAAC;AAE/F,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAE7D;AAED,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAE1C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC9C,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;CAC9C,CAAC,CAKD"}
@@ -2,8 +2,11 @@
2
2
  /**
3
3
  * Registry of supported agent targets.
4
4
  *
5
- * Currently Claude-only. The interface is preserved so re-adding another
6
- * agent is one new file in `targets/` plus one entry below.
5
+ * Claude Code plus the one ratified exception, Gemini CLI (GEMINI-TARGET-DOC).
6
+ * Registration does NOT make a target install by default — `specship install`
7
+ * writes Claude alone unless `--target` names another (REQ-GEMINI-002.A4).
8
+ * Being here means the target participates in the contract suite, in
9
+ * `detectAll`, and in the uninstall sweep.
7
10
  */
8
11
  Object.defineProperty(exports, "__esModule", { value: true });
9
12
  exports.ALL_TARGETS = void 0;
@@ -11,7 +14,8 @@ exports.getTarget = getTarget;
11
14
  exports.listTargetIds = listTargetIds;
12
15
  exports.detectAll = detectAll;
13
16
  const claude_1 = require("./claude");
14
- exports.ALL_TARGETS = Object.freeze([claude_1.claudeTarget]);
17
+ const gemini_1 = require("./gemini");
18
+ exports.ALL_TARGETS = Object.freeze([claude_1.claudeTarget, gemini_1.geminiTarget]);
15
19
  function getTarget(id) {
16
20
  return exports.ALL_TARGETS.find((t) => t.id === id);
17
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/installer/targets/registry.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAOH,8BAEC;AAED,sCAEC;AAMD,8BAQC;AAxBD,qCAAwC;AAE3B,QAAA,WAAW,GAA2B,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAY,CAAC,CAAC,CAAC;AAEjF,SAAgB,SAAS,CAAC,EAAU;IAClC,OAAO,mBAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,aAAa;IAC3B,OAAO,mBAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,GAAa;IAIrC,OAAO,mBAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM;QACN,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;KAC9B,CAAC,CAAC,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/installer/targets/registry.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAQH,8BAEC;AAED,sCAEC;AAMD,8BAQC;AAzBD,qCAAwC;AACxC,qCAAwC;AAE3B,QAAA,WAAW,GAA2B,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAY,EAAE,qBAAY,CAAC,CAAC,CAAC;AAE/F,SAAgB,SAAS,CAAC,EAAU;IAClC,OAAO,mBAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,aAAa;IAC3B,OAAO,mBAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,GAAa;IAIrC,OAAO,mBAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM;QACN,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;KAC9B,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -8,8 +8,12 @@
8
8
  * + one registry entry. See `targets/registry.ts`.
9
9
  */
10
10
  export type Location = 'global' | 'local';
11
- /** Stable string id for the lone supported target. */
12
- export type TargetId = 'claude';
11
+ /**
12
+ * Stable string ids for supported targets. `gemini` (GEMINI-TARGET-DOC) is
13
+ * declared here from Phase 0 on, but only its `printConfig` is implemented
14
+ * and it is not in `ALL_TARGETS` yet — see `targets/gemini.ts`.
15
+ */
16
+ export type TargetId = 'claude' | 'gemini';
13
17
  /**
14
18
  * Result of `target.detect(location)`.
15
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/installer/targets/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C,sDAAsD;AACtD,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;KAChF,CAAC,CAAC;IACH,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,eAAe,CAAC;IACvC,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,GAAG,WAAW,CAAC;IAC1D;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC;IACtC;;;;OAIG;IACH,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,oEAAoE;IACpE,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,CAAC;CACxC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/installer/targets/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;KAChF,CAAC,CAAC;IACH,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,eAAe,CAAC;IACvC,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,GAAG,WAAW,CAAC;IAC1D;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAAC;IACtC;;;;OAIG;IACH,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,oEAAoE;IACpE,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,EAAE,CAAC;CACxC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Auto-publish specs to JIRA on sync (REQ-JIRATEAM-002).
3
+ *
4
+ * With a repo-committed JIRA project binding in `specship.config.json`
5
+ * (REQ-JIRATEAM-001), every cleanly-indexed requirement spec is
6
+ * published/refreshed to JIRA during `sync` — reusing the existing
7
+ * idempotent `publishSpecToJira` primitive so the fingerprint short-circuits
8
+ * unchanged specs, missing Sub-tasks are added, and `writeBackJiraIdentity`
9
+ * stamps the `jira_issue:` back into the file.
10
+ *
11
+ * A5 gate: with NO binding, this returns immediately without constructing a
12
+ * JIRA client or reading credentials — zero JIRA calls. Even with a binding,
13
+ * a repo whose user has no credentials configured degrades to a single
14
+ * per-sync note (never crashes, never blocks sync). Per-spec publish failures
15
+ * are collected as `failed` results; siblings still publish.
16
+ */
17
+ import { resolveJiraCredentials } from './config';
18
+ import { type PublishJiraClient } from './publish';
19
+ export type AutoPublishStatus = 'published' | 'unchanged' | 'opted_out' | 'failed';
20
+ export interface AutoPublishResultRow {
21
+ specId: string;
22
+ status: AutoPublishStatus;
23
+ jiraKey?: string;
24
+ subtasksCreated?: number;
25
+ note?: string;
26
+ }
27
+ export interface AutoPublishReport {
28
+ /**
29
+ * Present when the pass short-circuited before touching JIRA:
30
+ * - 'unbound' → no repo binding (A5)
31
+ * - 'no-credentials' → binding present but user credentials missing (A4-like)
32
+ */
33
+ skipped?: 'unbound' | 'no-credentials';
34
+ /** Human-readable note when `skipped` is set (surfaced by the caller). */
35
+ note?: string;
36
+ results: AutoPublishResultRow[];
37
+ }
38
+ /** The structural slice of SpecQueries this pass reads. */
39
+ export interface AutoPublishSpecQueries {
40
+ getAllSpecs(): Array<{
41
+ id: string;
42
+ kind: string;
43
+ title: string;
44
+ body: string;
45
+ sourcePath: string;
46
+ }>;
47
+ getSpecsByParent(id: string): Array<{
48
+ id: string;
49
+ kind: string;
50
+ title: string;
51
+ body: string;
52
+ }>;
53
+ }
54
+ export interface AutoPublishDeps {
55
+ specQueries: AutoPublishSpecQueries;
56
+ projectRoot: string;
57
+ /** Test seam: build a JIRA client from resolved creds. Default: JiraClient. */
58
+ makeClient?: (creds: ReturnType<typeof resolveJiraCredentials>) => PublishJiraClient;
59
+ }
60
+ /**
61
+ * Publish/refresh every requirement spec to JIRA when a repo binding is
62
+ * present. Never throws — every failure lands in the returned report.
63
+ */
64
+ export declare function autoPublishSpecsOnSync(deps: AutoPublishDeps): Promise<AutoPublishReport>;
65
+ /** One-line summary suitable for CLI output. */
66
+ export declare function summarizeAutoPublishReport(report: AutoPublishReport): string;
67
+ //# sourceMappingURL=auto-publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-publish.d.ts","sourceRoot":"","sources":["../../src/jira/auto-publish.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGlD,OAAO,EAOL,KAAK,iBAAiB,EAEvB,MAAM,WAAW,CAAC;AAKnB,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEnF,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,gBAAgB,CAAC;IACvC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAED,2DAA2D;AAC3D,MAAM,WAAW,sBAAsB;IACrC,WAAW,IAAI,KAAK,CAAC;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC;QAClC,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,sBAAsB,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,KAAK,iBAAiB,CAAC;CACtF;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,iBAAiB,CAAC,CA4K5B;AA8BD,gDAAgD;AAChD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAmB5E"}
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ /**
3
+ * Auto-publish specs to JIRA on sync (REQ-JIRATEAM-002).
4
+ *
5
+ * With a repo-committed JIRA project binding in `specship.config.json`
6
+ * (REQ-JIRATEAM-001), every cleanly-indexed requirement spec is
7
+ * published/refreshed to JIRA during `sync` — reusing the existing
8
+ * idempotent `publishSpecToJira` primitive so the fingerprint short-circuits
9
+ * unchanged specs, missing Sub-tasks are added, and `writeBackJiraIdentity`
10
+ * stamps the `jira_issue:` back into the file.
11
+ *
12
+ * A5 gate: with NO binding, this returns immediately without constructing a
13
+ * JIRA client or reading credentials — zero JIRA calls. Even with a binding,
14
+ * a repo whose user has no credentials configured degrades to a single
15
+ * per-sync note (never crashes, never blocks sync). Per-spec publish failures
16
+ * are collected as `failed` results; siblings still publish.
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.autoPublishSpecsOnSync = autoPublishSpecsOnSync;
53
+ exports.summarizeAutoPublishReport = summarizeAutoPublishReport;
54
+ const path = __importStar(require("path"));
55
+ const config_1 = require("./config");
56
+ const repo_config_1 = require("./repo-config");
57
+ const client_1 = require("./client");
58
+ const publish_1 = require("./publish");
59
+ const spec_writer_1 = require("./spec-writer");
60
+ const milestone_comment_1 = require("./milestone-comment");
61
+ const fs = __importStar(require("fs"));
62
+ /**
63
+ * Publish/refresh every requirement spec to JIRA when a repo binding is
64
+ * present. Never throws — every failure lands in the returned report.
65
+ */
66
+ async function autoPublishSpecsOnSync(deps) {
67
+ // A5: no repo binding → do NOTHING. No client construction, no credential
68
+ // read, no JIRA call.
69
+ const binding = (0, repo_config_1.loadRepoJiraBinding)(deps.projectRoot);
70
+ if (!binding.binding) {
71
+ return { skipped: 'unbound', results: [] };
72
+ }
73
+ // Bound repo without user credentials: one per-sync note, never a crash.
74
+ let creds;
75
+ try {
76
+ creds = (0, config_1.resolveJiraCredentials)(deps.projectRoot);
77
+ }
78
+ catch (err) {
79
+ const msg = err instanceof Error ? err.message : String(err);
80
+ return {
81
+ skipped: 'no-credentials',
82
+ note: msg,
83
+ results: [],
84
+ };
85
+ }
86
+ const projectKey = creds.project;
87
+ if (!projectKey) {
88
+ return {
89
+ skipped: 'no-credentials',
90
+ note: 'No JIRA project resolved from binding or user config.',
91
+ results: [],
92
+ };
93
+ }
94
+ const client = deps.makeClient
95
+ ? deps.makeClient(creds)
96
+ : new client_1.JiraClient(creds);
97
+ const results = [];
98
+ const requirements = deps.specQueries.getAllSpecs().filter((s) => s.kind === 'requirement');
99
+ // Requirements per file (REQ-JIRATEAM-010): a file holding >1 requirement
100
+ // keys each one's JIRA identity per-requirement — the plain file-level
101
+ // `jira_issue:` is only trusted when the file has exactly one.
102
+ const reqsPerFile = new Map();
103
+ for (const s of requirements) {
104
+ reqsPerFile.set(s.sourcePath, (reqsPerFile.get(s.sourcePath) ?? 0) + 1);
105
+ }
106
+ for (const spec of requirements) {
107
+ const absPath = path.isAbsolute(spec.sourcePath)
108
+ ? spec.sourcePath
109
+ : path.join(deps.projectRoot, spec.sourcePath);
110
+ // Per-spec frontmatter opt-out (A3). Note: readFrontmatterValue reads
111
+ // the file's leading frontmatter block only — a `jira_publish:` mention
112
+ // in the body cannot opt a spec out.
113
+ let fileContent;
114
+ try {
115
+ fileContent = fs.readFileSync(absPath, 'utf8');
116
+ }
117
+ catch (err) {
118
+ const msg = err instanceof Error ? err.message : String(err);
119
+ results.push({ specId: spec.id, status: 'failed', note: msg });
120
+ continue;
121
+ }
122
+ const optOut = (0, publish_1.readFrontmatterValue)(fileContent, 'jira_publish');
123
+ if (optOut !== null && optOut.toLowerCase() === 'false') {
124
+ results.push({ specId: spec.id, status: 'opted_out' });
125
+ continue;
126
+ }
127
+ const acceptance = deps.specQueries
128
+ .getSpecsByParent(spec.id)
129
+ .filter((c) => c.kind === 'acceptance')
130
+ .map((c) => ({ id: c.id, text: (c.title || c.body || '').trim() }))
131
+ .filter((c) => c.text.length > 0);
132
+ const source = {
133
+ specId: spec.id,
134
+ title: spec.title,
135
+ body: spec.body ?? '',
136
+ specRelPath: spec.sourcePath,
137
+ acceptance,
138
+ };
139
+ // REQ-JIRATEAM-010.A4: never resolve another requirement's issue. The
140
+ // per-requirement key wins; the plain file-level key applies only when
141
+ // this requirement is the file's sole one.
142
+ const soleRequirement = (reqsPerFile.get(spec.sourcePath) ?? 1) === 1;
143
+ const perKeys = (0, publish_1.perSpecFrontmatterKeys)(spec.id);
144
+ const existingKey = (0, publish_1.readFrontmatterValue)(fileContent, perKeys.issueKey) ??
145
+ (soleRequirement ? (0, spec_writer_1.readSpecJiraKey)(absPath) : null);
146
+ // REQ-JIRATEAM-006: resolve the epic anchor — frontmatter first, then
147
+ // the binding default. `requireEpic` is off here so a bound repo whose
148
+ // spec pre-dates board-first intake doesn't hard-fail on every sync;
149
+ // the intake gate is what fences new specs. A refresh with no epic and
150
+ // no live parent still publishes unchanged Story text.
151
+ const specEpicKey = (0, publish_1.readFrontmatterValue)(fileContent, 'epicKey') ??
152
+ binding.binding.epicKey ??
153
+ undefined;
154
+ // Fingerprint short-circuit (A1): an already-published spec whose
155
+ // rendered Story fields match the last-published fingerprint performs
156
+ // ZERO JIRA writes — no updateIssue, no getIssue, no Sub-task probe.
157
+ if (existingKey) {
158
+ const priorFp = (0, publish_1.readFrontmatterValue)(fileContent, perKeys.fingerprintKey) ??
159
+ (soleRequirement
160
+ ? (0, publish_1.readFrontmatterValue)(fileContent, 'jira_fingerprint')
161
+ : null);
162
+ if (priorFp) {
163
+ const fields = (0, publish_1.buildIssueFields)(source);
164
+ const nextFp = (0, publish_1.issueContentFingerprint)(fields.summary, fields.description);
165
+ if (priorFp === nextFp) {
166
+ results.push({
167
+ specId: spec.id,
168
+ status: 'unchanged',
169
+ jiraKey: existingKey,
170
+ subtasksCreated: 0,
171
+ });
172
+ continue;
173
+ }
174
+ }
175
+ }
176
+ try {
177
+ const result = await (0, publish_1.publishSpecToJira)(client, source, {
178
+ projectKey,
179
+ // REQ-JIRATEAM-009: honor the binding's issue-type overrides — a
180
+ // team-managed project without a `Story` type publishes as `Task`.
181
+ ...(0, repo_config_1.bindingIssueTypes)(deps.projectRoot),
182
+ ...(specEpicKey ? { epicKey: specEpicKey } : {}),
183
+ }, existingKey);
184
+ // Frontmatter write-back stamps jira_issue: and the fingerprint. Never
185
+ // re-id or rename here — auto-publish runs on every sync and must be
186
+ // safe on files that already carry links or share a file with siblings.
187
+ (0, publish_1.writeBackJiraIdentity)(absPath, result.key, {
188
+ fingerprint: result.fingerprint,
189
+ reId: null,
190
+ renameTo: null,
191
+ ...(result.epicKey ? { epicKey: result.epicKey } : {}),
192
+ // REQ-JIRATEAM-010.A2: per-requirement keys in multi-REQ files.
193
+ ...(soleRequirement ? {} : { forSpecId: spec.id }),
194
+ });
195
+ const status = result.created || result.subtasksCreated > 0 ? 'published' : 'unchanged';
196
+ results.push({
197
+ specId: spec.id,
198
+ status,
199
+ jiraKey: result.key,
200
+ subtasksCreated: result.subtasksCreated,
201
+ });
202
+ // Milestone: spec_published (REQ-JIRATEAM-003.A1). Idempotent — the
203
+ // dispatcher itself skips when the marker already exists, so calling
204
+ // it on every publish (including refresh) is safe. Soft-fail so a
205
+ // milestone comment issue never breaks auto-publish (A3).
206
+ if (status === 'published') {
207
+ await postMilestoneOnSuccess(client, result.key, spec.id, deps.projectRoot);
208
+ }
209
+ }
210
+ catch (err) {
211
+ // A4: one spec's failure never blocks the sync or its siblings.
212
+ const msg = err instanceof Error ? err.message : String(err);
213
+ results.push({ specId: spec.id, status: 'failed', note: msg });
214
+ }
215
+ }
216
+ return { results };
217
+ }
218
+ /**
219
+ * Best-effort spec_published milestone comment. A fake `PublishJiraClient`
220
+ * (some tests) doesn't implement the milestone methods — probe first and
221
+ * silently skip when they're missing, so test seams stay narrow.
222
+ */
223
+ async function postMilestoneOnSuccess(client, issueKey, specId, projectRoot) {
224
+ const maybe = client;
225
+ if (typeof maybe.listCommentsDetailed !== 'function' ||
226
+ typeof maybe.addComment !== 'function' ||
227
+ typeof maybe.updateComment !== 'function') {
228
+ return;
229
+ }
230
+ await (0, milestone_comment_1.postMilestoneComment)(maybe, issueKey, 'spec_published', { specId }, { projectRoot });
231
+ }
232
+ /** One-line summary suitable for CLI output. */
233
+ function summarizeAutoPublishReport(report) {
234
+ if (report.skipped === 'unbound')
235
+ return '';
236
+ if (report.skipped === 'no-credentials') {
237
+ return `JIRA auto-publish skipped: ${report.note ?? 'no credentials'}`;
238
+ }
239
+ let published = 0;
240
+ let unchanged = 0;
241
+ let optedOut = 0;
242
+ let failed = 0;
243
+ for (const r of report.results) {
244
+ if (r.status === 'published')
245
+ published++;
246
+ else if (r.status === 'unchanged')
247
+ unchanged++;
248
+ else if (r.status === 'opted_out')
249
+ optedOut++;
250
+ else if (r.status === 'failed')
251
+ failed++;
252
+ }
253
+ return (`JIRA auto-publish: ${published} published, ${unchanged} unchanged, ` +
254
+ `${optedOut} opted out, ${failed} failed`);
255
+ }
256
+ //# sourceMappingURL=auto-publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-publish.js","sourceRoot":"","sources":["../../src/jira/auto-publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Board-first anchor resolution (REQ-JIRATEAM-007).
3
+ *
4
+ * Every work-creating flow on a JIRA-bound repo MUST be anchored to a JIRA
5
+ * issue. This module answers ONE question for the CLI, MCP tool, and slash
6
+ * command that gate work: "given cwd + optional explicit/picked issueKey,
7
+ * do we have an anchor, refuse, or should we no-op (unbound)?"
8
+ *
9
+ * The return is a THREE-way discriminated union — `unbound` is first-class
10
+ * so callers on an unbound repo just proceed unchanged.
11
+ *
12
+ * When a bound repo refuses because no issue was picked, we ALSO surface the
13
+ * epic-scoped pickable work in the refusal payload so the pick can happen in
14
+ * the same round-trip (spec correction: A1 requires the refusal to LIST the
15
+ * pickable work, not just hint at it).
16
+ */
17
+ import type { JiraIssue } from './types';
18
+ export type WorkAnchor = {
19
+ issueKey: string;
20
+ summary: string;
21
+ source: 'explicit' | 'picked' | 'epic-child';
22
+ };
23
+ export type AnchorUnbound = {
24
+ status: 'unbound';
25
+ };
26
+ export type AnchorAnchored = {
27
+ status: 'anchored';
28
+ anchor: WorkAnchor;
29
+ };
30
+ export type AnchorRefused = {
31
+ status: 'refused';
32
+ reason: 'no-pick' | 'no-epic-no-pick';
33
+ fixHint: string;
34
+ /** Epic-scoped open stories/tasks — populated on `no-pick` so the caller can
35
+ * render the picker in the same turn (A1). */
36
+ pickable?: JiraIssue[];
37
+ };
38
+ export type AnchorResolution = AnchorUnbound | AnchorAnchored | AnchorRefused;
39
+ /** Minimal client seam so tests inject a fake and never touch a real host. */
40
+ export interface AnchorJiraClient {
41
+ listMyIssues(opts?: {
42
+ project?: string;
43
+ sprint?: 'active';
44
+ epicKey?: string;
45
+ }): Promise<{
46
+ ok: true;
47
+ issues: JiraIssue[];
48
+ }>;
49
+ getIssue(key: string): Promise<{
50
+ ok: true;
51
+ issue: JiraIssue;
52
+ }>;
53
+ }
54
+ export interface ResolveWorkAnchorOptions {
55
+ cwd: string;
56
+ explicitIssueKey?: string;
57
+ pickedIssueKey?: string;
58
+ /** Test seam. Default: resolve credentials + new JiraClient. */
59
+ makeClient?: () => AnchorJiraClient;
60
+ }
61
+ export declare function resolveWorkAnchor(opts: ResolveWorkAnchorOptions): Promise<AnchorResolution>;
62
+ /** Canonical human-facing refusal message for CLI/MCP callers. */
63
+ export declare function formatRefusal(res: AnchorRefused): string;
64
+ //# sourceMappingURL=board-first.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-first.d.ts","sourceRoot":"","sources":["../../src/jira/board-first.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAClD,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AACxE,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB;mDAC+C;IAC/C,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,CAAC;AAE9E,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,IAAI,CAAC,EAAE;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,SAAS,EAAE,CAAA;KAAE,CAAC,CAAC;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,gBAAgB,CAAC;CACrC;AAOD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAoD3B;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAaxD"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /**
3
+ * Board-first anchor resolution (REQ-JIRATEAM-007).
4
+ *
5
+ * Every work-creating flow on a JIRA-bound repo MUST be anchored to a JIRA
6
+ * issue. This module answers ONE question for the CLI, MCP tool, and slash
7
+ * command that gate work: "given cwd + optional explicit/picked issueKey,
8
+ * do we have an anchor, refuse, or should we no-op (unbound)?"
9
+ *
10
+ * The return is a THREE-way discriminated union — `unbound` is first-class
11
+ * so callers on an unbound repo just proceed unchanged.
12
+ *
13
+ * When a bound repo refuses because no issue was picked, we ALSO surface the
14
+ * epic-scoped pickable work in the refusal payload so the pick can happen in
15
+ * the same round-trip (spec correction: A1 requires the refusal to LIST the
16
+ * pickable work, not just hint at it).
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.resolveWorkAnchor = resolveWorkAnchor;
20
+ exports.formatRefusal = formatRefusal;
21
+ const repo_config_1 = require("./repo-config");
22
+ const config_1 = require("./config");
23
+ const client_1 = require("./client");
24
+ function defaultMakeClient() {
25
+ const creds = (0, config_1.resolveJiraCredentials)();
26
+ return new client_1.JiraClient(creds);
27
+ }
28
+ async function resolveWorkAnchor(opts) {
29
+ const { binding } = (0, repo_config_1.loadRepoJiraBinding)(opts.cwd);
30
+ if (!binding)
31
+ return { status: 'unbound' };
32
+ const makeClient = opts.makeClient ?? defaultMakeClient;
33
+ const explicit = opts.explicitIssueKey?.trim();
34
+ const picked = opts.pickedIssueKey?.trim();
35
+ const key = explicit || picked;
36
+ if (key) {
37
+ const client = makeClient();
38
+ const res = await client.getIssue(key);
39
+ return {
40
+ status: 'anchored',
41
+ anchor: {
42
+ issueKey: res.issue.key,
43
+ summary: res.issue.summary,
44
+ source: explicit ? 'explicit' : 'picked',
45
+ },
46
+ };
47
+ }
48
+ if (!binding.epicKey) {
49
+ return {
50
+ status: 'refused',
51
+ reason: 'no-epic-no-pick',
52
+ fixHint: 'Set jira.epicKey in specship.config.json (or pick an issue with specship_jira_pick <KEY>).',
53
+ };
54
+ }
55
+ // Bound + epic + no pick → list the epic's open stories/tasks so the caller
56
+ // can render the pick in the same round-trip (A1).
57
+ let pickable = [];
58
+ try {
59
+ const client = makeClient();
60
+ const res = await client.listMyIssues({
61
+ project: binding.projectKey,
62
+ epicKey: binding.epicKey,
63
+ });
64
+ pickable = res.issues;
65
+ }
66
+ catch {
67
+ // Degrade to a refusal without pickable list — the fixHint still names
68
+ // the exact next step.
69
+ }
70
+ return {
71
+ status: 'refused',
72
+ reason: 'no-pick',
73
+ fixHint: 'Run specship_jira_issues to list the epic\'s open work, then specship_jira_pick <KEY> to anchor this run.',
74
+ pickable,
75
+ };
76
+ }
77
+ /** Canonical human-facing refusal message for CLI/MCP callers. */
78
+ function formatRefusal(res) {
79
+ const header = res.reason === 'no-epic-no-pick'
80
+ ? 'Work refused: no JIRA anchor — no epic bound and no issue picked.'
81
+ : 'Work refused: no JIRA anchor — pick an issue under the bound epic.';
82
+ const lines = [header, res.fixHint];
83
+ if (res.pickable && res.pickable.length) {
84
+ lines.push('', 'Pickable issues:');
85
+ for (const i of res.pickable) {
86
+ lines.push(`- ${i.key} — ${i.summary} [${i.status}]`);
87
+ }
88
+ }
89
+ return lines.join('\n');
90
+ }
91
+ //# sourceMappingURL=board-first.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-first.js","sourceRoot":"","sources":["../../src/jira/board-first.ts"],"names":[],"mappings":"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"}