hyper-pm 0.1.2 → 0.1.3

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 (4) hide show
  1. package/README.md +16 -15
  2. package/dist/index.cjs +493 -182
  3. package/dist/main.cjs +493 -182
  4. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -12671,6 +12671,336 @@ var listActiveTicketSummaries = (projection, options) => {
12671
12671
  });
12672
12672
  };
12673
12673
 
12674
+ // src/lib/github-issue-body.ts
12675
+ var FENCE_JSON_RE = /```json\s*([\s\S]*?)```/i;
12676
+ var parseHyperPmFenceObject = (body) => {
12677
+ const fence = body.match(FENCE_JSON_RE);
12678
+ if (!fence?.[1]) return void 0;
12679
+ try {
12680
+ const data = JSON.parse(fence[1].trim());
12681
+ if (typeof data !== "object" || data === null) return void 0;
12682
+ return data;
12683
+ } catch {
12684
+ return void 0;
12685
+ }
12686
+ };
12687
+ var parseHyperPmIdFromIssueBody = (body) => {
12688
+ const meta = parseHyperPmFenceObject(body);
12689
+ if (meta === void 0) return void 0;
12690
+ const id = meta["hyper_pm_id"];
12691
+ return typeof id === "string" ? id : void 0;
12692
+ };
12693
+ var extractDescriptionBeforeFirstFence = (body) => {
12694
+ const fenceIdx = body.indexOf("```");
12695
+ if (fenceIdx === -1) {
12696
+ return body.trim();
12697
+ }
12698
+ return body.slice(0, fenceIdx).trim();
12699
+ };
12700
+ var inboundTicketPlanningPayloadFromFenceMeta = (meta) => {
12701
+ const out = {};
12702
+ if (Object.prototype.hasOwnProperty.call(meta, "priority")) {
12703
+ const v = meta["priority"];
12704
+ if (v === null) {
12705
+ out["priority"] = null;
12706
+ } else if (typeof v === "string") {
12707
+ const p = tryParseTicketPriority(v);
12708
+ if (p !== void 0) {
12709
+ out["priority"] = p;
12710
+ }
12711
+ }
12712
+ }
12713
+ if (Object.prototype.hasOwnProperty.call(meta, "size")) {
12714
+ const v = meta["size"];
12715
+ if (v === null) {
12716
+ out["size"] = null;
12717
+ } else if (typeof v === "string") {
12718
+ const s = tryParseTicketSize(v);
12719
+ if (s !== void 0) {
12720
+ out["size"] = s;
12721
+ }
12722
+ }
12723
+ }
12724
+ if (Object.prototype.hasOwnProperty.call(meta, "estimate")) {
12725
+ const v = meta["estimate"];
12726
+ if (v === null) {
12727
+ out["estimate"] = null;
12728
+ } else if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
12729
+ out["estimate"] = v;
12730
+ }
12731
+ }
12732
+ if (Object.prototype.hasOwnProperty.call(meta, "start_work_at")) {
12733
+ const v = meta["start_work_at"];
12734
+ if (v === null) {
12735
+ out["startWorkAt"] = null;
12736
+ } else if (typeof v === "string") {
12737
+ const t = v.trim();
12738
+ if (t !== "" && Number.isFinite(Date.parse(t))) {
12739
+ out["startWorkAt"] = t;
12740
+ }
12741
+ }
12742
+ }
12743
+ if (Object.prototype.hasOwnProperty.call(meta, "target_finish_at")) {
12744
+ const v = meta["target_finish_at"];
12745
+ if (v === null) {
12746
+ out["targetFinishAt"] = null;
12747
+ } else if (typeof v === "string") {
12748
+ const t = v.trim();
12749
+ if (t !== "" && Number.isFinite(Date.parse(t))) {
12750
+ out["targetFinishAt"] = t;
12751
+ }
12752
+ }
12753
+ }
12754
+ return out;
12755
+ };
12756
+ var buildGithubIssueBody = (params) => {
12757
+ const meta = {
12758
+ hyper_pm_id: params.hyperPmId,
12759
+ type: params.type,
12760
+ parent_ids: params.parentIds
12761
+ };
12762
+ if (params.type === "ticket" && params.ticketPlanning !== void 0) {
12763
+ const p = params.ticketPlanning;
12764
+ if (p.priority !== void 0) {
12765
+ meta.priority = p.priority;
12766
+ }
12767
+ if (p.size !== void 0) {
12768
+ meta.size = p.size;
12769
+ }
12770
+ if (p.estimate !== void 0) {
12771
+ meta.estimate = p.estimate;
12772
+ }
12773
+ if (p.startWorkAt !== void 0) {
12774
+ meta.start_work_at = p.startWorkAt;
12775
+ }
12776
+ if (p.targetFinishAt !== void 0) {
12777
+ meta.target_finish_at = p.targetFinishAt;
12778
+ }
12779
+ }
12780
+ return `${params.description.trim()}
12781
+
12782
+ \`\`\`json
12783
+ ${JSON.stringify(meta, null, 2)}
12784
+ \`\`\`
12785
+ `;
12786
+ };
12787
+ var ticketPlanningForGithubIssueBody = (ticket) => {
12788
+ const out = {};
12789
+ if (ticket.priority !== void 0) {
12790
+ out.priority = ticket.priority;
12791
+ }
12792
+ if (ticket.size !== void 0) {
12793
+ out.size = ticket.size;
12794
+ }
12795
+ if (ticket.estimate !== void 0) {
12796
+ out.estimate = ticket.estimate;
12797
+ }
12798
+ if (ticket.startWorkAt !== void 0) {
12799
+ out.startWorkAt = ticket.startWorkAt;
12800
+ }
12801
+ if (ticket.targetFinishAt !== void 0) {
12802
+ out.targetFinishAt = ticket.targetFinishAt;
12803
+ }
12804
+ return Object.keys(out).length > 0 ? out : void 0;
12805
+ };
12806
+
12807
+ // src/lib/github-issue-labels.ts
12808
+ var RESERVED_LOWER = /* @__PURE__ */ new Set(["hyper-pm", "ticket"]);
12809
+ var GITHUB_LABEL_NAME_MAX_LENGTH = 50;
12810
+ var isReservedHyperPmGithubLabel = (name) => {
12811
+ return RESERVED_LOWER.has(name.trim().toLowerCase());
12812
+ };
12813
+ var labelNameFromGithubLabelEntry = (entry) => {
12814
+ if (typeof entry === "string") {
12815
+ const t = entry.trim();
12816
+ return t === "" ? void 0 : t;
12817
+ }
12818
+ if (typeof entry === "object" && entry !== null && "name" in entry) {
12819
+ const n = entry.name;
12820
+ if (typeof n !== "string") return void 0;
12821
+ const t = n.trim();
12822
+ return t === "" ? void 0 : t;
12823
+ }
12824
+ return void 0;
12825
+ };
12826
+ var ticketLabelsFromGithubIssueLabels = (labels) => {
12827
+ if (!Array.isArray(labels)) {
12828
+ return [];
12829
+ }
12830
+ const raw = [];
12831
+ for (const item of labels) {
12832
+ const n = labelNameFromGithubLabelEntry(item);
12833
+ if (n === void 0) continue;
12834
+ if (isReservedHyperPmGithubLabel(n)) continue;
12835
+ raw.push(n);
12836
+ }
12837
+ return normalizeTicketLabelList(raw);
12838
+ };
12839
+ var mergeOutboundGithubIssueLabelsForTicket = (ticketLabels) => {
12840
+ const base = ["hyper-pm", "ticket"];
12841
+ const seen = new Set(base.map((x) => x.toLowerCase()));
12842
+ const out = [...base];
12843
+ const norm = normalizeTicketLabelList(ticketLabels ?? []);
12844
+ for (const lab of norm) {
12845
+ if (lab.length > GITHUB_LABEL_NAME_MAX_LENGTH) {
12846
+ continue;
12847
+ }
12848
+ const low = lab.toLowerCase();
12849
+ if (seen.has(low)) continue;
12850
+ seen.add(low);
12851
+ out.push(lab);
12852
+ }
12853
+ return out;
12854
+ };
12855
+
12856
+ // src/cli/github-issue-import.ts
12857
+ var collectLinkedGithubIssueNumbers = (projection) => {
12858
+ const out = /* @__PURE__ */ new Set();
12859
+ for (const ticket of projection.tickets.values()) {
12860
+ if (ticket.deleted) continue;
12861
+ const n = ticket.githubIssueNumber;
12862
+ if (n !== void 0 && Number.isFinite(n)) {
12863
+ out.add(n);
12864
+ }
12865
+ }
12866
+ return out;
12867
+ };
12868
+ var stripHyperPmGithubIssueTitle = (title) => title.replace(/^\[hyper-pm\]\s*/i, "").trim();
12869
+ var tryParseGithubImportListState = (raw) => {
12870
+ if (raw === void 0 || raw === "") return "all";
12871
+ const t = raw.trim().toLowerCase();
12872
+ if (t === "open" || t === "closed" || t === "all") return t;
12873
+ return void 0;
12874
+ };
12875
+ var parseGithubImportIssueNumberSet = (raw) => {
12876
+ if (raw === void 0 || raw.length === 0) return void 0;
12877
+ const out = /* @__PURE__ */ new Set();
12878
+ for (const piece of raw) {
12879
+ for (const token of piece.split(",")) {
12880
+ const t = token.trim();
12881
+ if (t === "") continue;
12882
+ const n = Number.parseInt(t, 10);
12883
+ if (!Number.isFinite(n) || n < 1) {
12884
+ throw new Error(`Invalid --issue value: ${JSON.stringify(token)}`);
12885
+ }
12886
+ out.add(n);
12887
+ }
12888
+ }
12889
+ if (out.size === 0) {
12890
+ throw new Error("No valid --issue numbers after parsing flags");
12891
+ }
12892
+ return out;
12893
+ };
12894
+ var ticketCreatePlanningFragmentFromFenceMeta = (meta) => {
12895
+ if (meta === void 0) return {};
12896
+ const src = inboundTicketPlanningPayloadFromFenceMeta(meta);
12897
+ const out = {};
12898
+ for (const [k, v] of Object.entries(src)) {
12899
+ if (v !== null && v !== void 0) {
12900
+ out[k] = v;
12901
+ }
12902
+ }
12903
+ return out;
12904
+ };
12905
+ var buildTicketCreatedPayloadBaseFromGithubIssue = (issue) => {
12906
+ const bodyText = issue.body ?? "";
12907
+ const title = stripHyperPmGithubIssueTitle(String(issue.title ?? ""));
12908
+ const desc = extractDescriptionBeforeFirstFence(bodyText);
12909
+ const payload = {
12910
+ title,
12911
+ body: desc
12912
+ };
12913
+ if (issue.state === "closed") {
12914
+ payload["state"] = "closed";
12915
+ }
12916
+ const assignee = assigneeFromGithubIssue(issue);
12917
+ if (assignee !== void 0 && assignee !== "") {
12918
+ payload["assignee"] = assignee;
12919
+ }
12920
+ const labels = ticketLabelsFromGithubIssueLabels(issue.labels);
12921
+ if (labels.length > 0) {
12922
+ payload["labels"] = labels;
12923
+ }
12924
+ const meta = parseHyperPmFenceObject(bodyText);
12925
+ Object.assign(payload, ticketCreatePlanningFragmentFromFenceMeta(meta));
12926
+ return payload;
12927
+ };
12928
+ var classifyGithubIssueForImport = (params) => {
12929
+ const num = params.issue.number;
12930
+ if (!Number.isFinite(num) || num < 1) {
12931
+ return {
12932
+ result: "skip",
12933
+ skip: { issueNumber: 0, reason: "issue_filter" }
12934
+ };
12935
+ }
12936
+ if (params.onlyIssueNumbers !== void 0 && !params.onlyIssueNumbers.has(num)) {
12937
+ return {
12938
+ result: "skip",
12939
+ skip: { issueNumber: num, reason: "issue_filter" }
12940
+ };
12941
+ }
12942
+ if (params.issue.pull_request !== void 0 && params.issue.pull_request !== null) {
12943
+ return {
12944
+ result: "skip",
12945
+ skip: { issueNumber: num, reason: "pull_request" }
12946
+ };
12947
+ }
12948
+ if (params.linkedNumbers.has(num)) {
12949
+ return {
12950
+ result: "skip",
12951
+ skip: { issueNumber: num, reason: "already_linked" }
12952
+ };
12953
+ }
12954
+ const body = params.issue.body ?? "";
12955
+ const hyperPmId = parseHyperPmIdFromIssueBody(body);
12956
+ if (hyperPmId !== void 0 && hyperPmId.trim() !== "") {
12957
+ const row = params.projection.tickets.get(hyperPmId);
12958
+ if (row !== void 0 && !row.deleted) {
12959
+ return {
12960
+ result: "skip",
12961
+ skip: { issueNumber: num, reason: "body_hyper_pm_existing_ticket" }
12962
+ };
12963
+ }
12964
+ return {
12965
+ result: "skip",
12966
+ skip: { issueNumber: num, reason: "body_hyper_pm_orphan_ref" }
12967
+ };
12968
+ }
12969
+ return {
12970
+ result: "candidate",
12971
+ ticketCreatedPayloadBase: buildTicketCreatedPayloadBaseFromGithubIssue(
12972
+ params.issue
12973
+ )
12974
+ };
12975
+ };
12976
+ var partitionGithubIssuesForImport = (params) => {
12977
+ const linkedNumbers = collectLinkedGithubIssueNumbers(params.projection);
12978
+ const candidates = [];
12979
+ const skipped = [];
12980
+ for (const issue of params.issues) {
12981
+ const r = classifyGithubIssueForImport({
12982
+ projection: params.projection,
12983
+ linkedNumbers,
12984
+ onlyIssueNumbers: params.onlyIssueNumbers,
12985
+ issue
12986
+ });
12987
+ if (r.result === "skip") {
12988
+ skipped.push(r.skip);
12989
+ } else {
12990
+ candidates.push({
12991
+ issueNumber: issue.number,
12992
+ ticketCreatedPayloadBase: r.ticketCreatedPayloadBase
12993
+ });
12994
+ }
12995
+ }
12996
+ return { candidates, skipped };
12997
+ };
12998
+ var mergeTicketImportCreatePayload = (ticketId, base, storyId) => {
12999
+ const storyTrimmed = storyId !== void 0 && storyId !== "" ? storyId.trim() : void 0;
13000
+ const storyPayload = storyTrimmed !== void 0 && storyTrimmed !== "" ? { storyId: storyTrimmed } : {};
13001
+ return { id: ticketId, ...base, ...storyPayload };
13002
+ };
13003
+
12674
13004
  // src/config/hyper-pm-config.ts
12675
13005
  var hyperPmConfigSchema = external_exports.object({
12676
13006
  schema: external_exports.literal(1),
@@ -13755,188 +14085,6 @@ var defaultGithubPrActivitySyncDeps = (params) => ({
13755
14085
  }
13756
14086
  });
13757
14087
 
13758
- // src/lib/github-issue-body.ts
13759
- var FENCE_JSON_RE = /```json\s*([\s\S]*?)```/i;
13760
- var parseHyperPmFenceObject = (body) => {
13761
- const fence = body.match(FENCE_JSON_RE);
13762
- if (!fence?.[1]) return void 0;
13763
- try {
13764
- const data = JSON.parse(fence[1].trim());
13765
- if (typeof data !== "object" || data === null) return void 0;
13766
- return data;
13767
- } catch {
13768
- return void 0;
13769
- }
13770
- };
13771
- var parseHyperPmIdFromIssueBody = (body) => {
13772
- const meta = parseHyperPmFenceObject(body);
13773
- if (meta === void 0) return void 0;
13774
- const id = meta["hyper_pm_id"];
13775
- return typeof id === "string" ? id : void 0;
13776
- };
13777
- var extractDescriptionBeforeFirstFence = (body) => {
13778
- const fenceIdx = body.indexOf("```");
13779
- if (fenceIdx === -1) {
13780
- return body.trim();
13781
- }
13782
- return body.slice(0, fenceIdx).trim();
13783
- };
13784
- var inboundTicketPlanningPayloadFromFenceMeta = (meta) => {
13785
- const out = {};
13786
- if (Object.prototype.hasOwnProperty.call(meta, "priority")) {
13787
- const v = meta["priority"];
13788
- if (v === null) {
13789
- out["priority"] = null;
13790
- } else if (typeof v === "string") {
13791
- const p = tryParseTicketPriority(v);
13792
- if (p !== void 0) {
13793
- out["priority"] = p;
13794
- }
13795
- }
13796
- }
13797
- if (Object.prototype.hasOwnProperty.call(meta, "size")) {
13798
- const v = meta["size"];
13799
- if (v === null) {
13800
- out["size"] = null;
13801
- } else if (typeof v === "string") {
13802
- const s = tryParseTicketSize(v);
13803
- if (s !== void 0) {
13804
- out["size"] = s;
13805
- }
13806
- }
13807
- }
13808
- if (Object.prototype.hasOwnProperty.call(meta, "estimate")) {
13809
- const v = meta["estimate"];
13810
- if (v === null) {
13811
- out["estimate"] = null;
13812
- } else if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
13813
- out["estimate"] = v;
13814
- }
13815
- }
13816
- if (Object.prototype.hasOwnProperty.call(meta, "start_work_at")) {
13817
- const v = meta["start_work_at"];
13818
- if (v === null) {
13819
- out["startWorkAt"] = null;
13820
- } else if (typeof v === "string") {
13821
- const t = v.trim();
13822
- if (t !== "" && Number.isFinite(Date.parse(t))) {
13823
- out["startWorkAt"] = t;
13824
- }
13825
- }
13826
- }
13827
- if (Object.prototype.hasOwnProperty.call(meta, "target_finish_at")) {
13828
- const v = meta["target_finish_at"];
13829
- if (v === null) {
13830
- out["targetFinishAt"] = null;
13831
- } else if (typeof v === "string") {
13832
- const t = v.trim();
13833
- if (t !== "" && Number.isFinite(Date.parse(t))) {
13834
- out["targetFinishAt"] = t;
13835
- }
13836
- }
13837
- }
13838
- return out;
13839
- };
13840
- var buildGithubIssueBody = (params) => {
13841
- const meta = {
13842
- hyper_pm_id: params.hyperPmId,
13843
- type: params.type,
13844
- parent_ids: params.parentIds
13845
- };
13846
- if (params.type === "ticket" && params.ticketPlanning !== void 0) {
13847
- const p = params.ticketPlanning;
13848
- if (p.priority !== void 0) {
13849
- meta.priority = p.priority;
13850
- }
13851
- if (p.size !== void 0) {
13852
- meta.size = p.size;
13853
- }
13854
- if (p.estimate !== void 0) {
13855
- meta.estimate = p.estimate;
13856
- }
13857
- if (p.startWorkAt !== void 0) {
13858
- meta.start_work_at = p.startWorkAt;
13859
- }
13860
- if (p.targetFinishAt !== void 0) {
13861
- meta.target_finish_at = p.targetFinishAt;
13862
- }
13863
- }
13864
- return `${params.description.trim()}
13865
-
13866
- \`\`\`json
13867
- ${JSON.stringify(meta, null, 2)}
13868
- \`\`\`
13869
- `;
13870
- };
13871
- var ticketPlanningForGithubIssueBody = (ticket) => {
13872
- const out = {};
13873
- if (ticket.priority !== void 0) {
13874
- out.priority = ticket.priority;
13875
- }
13876
- if (ticket.size !== void 0) {
13877
- out.size = ticket.size;
13878
- }
13879
- if (ticket.estimate !== void 0) {
13880
- out.estimate = ticket.estimate;
13881
- }
13882
- if (ticket.startWorkAt !== void 0) {
13883
- out.startWorkAt = ticket.startWorkAt;
13884
- }
13885
- if (ticket.targetFinishAt !== void 0) {
13886
- out.targetFinishAt = ticket.targetFinishAt;
13887
- }
13888
- return Object.keys(out).length > 0 ? out : void 0;
13889
- };
13890
-
13891
- // src/lib/github-issue-labels.ts
13892
- var RESERVED_LOWER = /* @__PURE__ */ new Set(["hyper-pm", "ticket"]);
13893
- var GITHUB_LABEL_NAME_MAX_LENGTH = 50;
13894
- var isReservedHyperPmGithubLabel = (name) => {
13895
- return RESERVED_LOWER.has(name.trim().toLowerCase());
13896
- };
13897
- var labelNameFromGithubLabelEntry = (entry) => {
13898
- if (typeof entry === "string") {
13899
- const t = entry.trim();
13900
- return t === "" ? void 0 : t;
13901
- }
13902
- if (typeof entry === "object" && entry !== null && "name" in entry) {
13903
- const n = entry.name;
13904
- if (typeof n !== "string") return void 0;
13905
- const t = n.trim();
13906
- return t === "" ? void 0 : t;
13907
- }
13908
- return void 0;
13909
- };
13910
- var ticketLabelsFromGithubIssueLabels = (labels) => {
13911
- if (!Array.isArray(labels)) {
13912
- return [];
13913
- }
13914
- const raw = [];
13915
- for (const item of labels) {
13916
- const n = labelNameFromGithubLabelEntry(item);
13917
- if (n === void 0) continue;
13918
- if (isReservedHyperPmGithubLabel(n)) continue;
13919
- raw.push(n);
13920
- }
13921
- return normalizeTicketLabelList(raw);
13922
- };
13923
- var mergeOutboundGithubIssueLabelsForTicket = (ticketLabels) => {
13924
- const base = ["hyper-pm", "ticket"];
13925
- const seen = new Set(base.map((x) => x.toLowerCase()));
13926
- const out = [...base];
13927
- const norm = normalizeTicketLabelList(ticketLabels ?? []);
13928
- for (const lab of norm) {
13929
- if (lab.length > GITHUB_LABEL_NAME_MAX_LENGTH) {
13930
- continue;
13931
- }
13932
- const low = lab.toLowerCase();
13933
- if (seen.has(low)) continue;
13934
- seen.add(low);
13935
- out.push(lab);
13936
- }
13937
- return out;
13938
- };
13939
-
13940
14088
  // src/sync/github-inbound-actor.ts
13941
14089
  var githubInboundActorFromIssue = (issue) => {
13942
14090
  const login = issue.user?.login?.trim();
@@ -15218,6 +15366,169 @@ ${body}`
15218
15366
  return { id: o.id, deleted: true };
15219
15367
  });
15220
15368
  });
15369
+ ticket.command("import-github").description(
15370
+ "Create local tickets for GitHub issues not yet represented in hyper-pm"
15371
+ ).option("--dry-run", "list import candidates without writing events", false).option(
15372
+ "--story <id>",
15373
+ "optional story id applied to every imported ticket (must exist)"
15374
+ ).option(
15375
+ "--state <s>",
15376
+ "GitHub list filter: open | closed | all (default all)",
15377
+ "all"
15378
+ ).option(
15379
+ "--issue <n>",
15380
+ "only consider these issue numbers (repeatable or comma-separated)",
15381
+ (value, previous) => [...previous, value],
15382
+ []
15383
+ ).action(async function() {
15384
+ const g = readGlobals(this);
15385
+ const o = this.opts();
15386
+ const listState = tryParseGithubImportListState(o.state);
15387
+ if (listState === void 0) {
15388
+ deps.error(
15389
+ `Invalid --state ${JSON.stringify(o.state)} (use open, closed, or all)`
15390
+ );
15391
+ deps.exit(ExitCode.UserError);
15392
+ }
15393
+ let onlyIssueNumbers;
15394
+ try {
15395
+ onlyIssueNumbers = parseGithubImportIssueNumberSet(o.issue);
15396
+ } catch (e) {
15397
+ deps.error(e instanceof Error ? e.message : String(e));
15398
+ deps.exit(ExitCode.UserError);
15399
+ }
15400
+ const repoRoot = await resolveRepoRoot(g.repo);
15401
+ const cfg = await loadMergedConfig(repoRoot, g);
15402
+ if (cfg.issueMapping !== "ticket") {
15403
+ deps.error(
15404
+ 'ticket import-github requires config issueMapping "ticket" (other mappings are not supported yet).'
15405
+ );
15406
+ deps.exit(ExitCode.UserError);
15407
+ }
15408
+ const githubToken = await resolveGithubTokenForSync({
15409
+ envToken: env.GITHUB_TOKEN,
15410
+ cwd: repoRoot
15411
+ });
15412
+ if (!githubToken) {
15413
+ deps.error(
15414
+ "GitHub auth required: set GITHUB_TOKEN or run `gh auth login`"
15415
+ );
15416
+ deps.exit(ExitCode.EnvironmentAuth);
15417
+ }
15418
+ const actor = await resolveCliActor({
15419
+ repoRoot,
15420
+ cliActor: g.actor,
15421
+ envActor: env.HYPER_PM_ACTOR
15422
+ });
15423
+ const tmpBase = g.tempDir ?? env.TMPDIR ?? (0, import_node_os2.tmpdir)();
15424
+ const session = await openDataBranchWorktree({
15425
+ repoRoot,
15426
+ dataBranch: cfg.dataBranch,
15427
+ tmpBase,
15428
+ keepWorktree: g.keepWorktree,
15429
+ runGit
15430
+ });
15431
+ try {
15432
+ const lines = await readAllEventLines(session.worktreePath);
15433
+ const proj = replayEvents(lines);
15434
+ const storyRaw = o.story;
15435
+ const storyTrimmed = storyRaw !== void 0 && storyRaw !== "" ? storyRaw.trim() : void 0;
15436
+ if (storyTrimmed !== void 0 && storyTrimmed !== "") {
15437
+ const storyRow = proj.stories.get(storyTrimmed);
15438
+ if (!storyRow || storyRow.deleted) {
15439
+ deps.error(`Story not found: ${storyTrimmed}`);
15440
+ deps.exit(ExitCode.UserError);
15441
+ }
15442
+ }
15443
+ try {
15444
+ const gitDerivedSlug = await tryReadGithubOwnerRepoSlugFromGit({
15445
+ repoRoot,
15446
+ remote: cfg.remote,
15447
+ runGit
15448
+ });
15449
+ const { owner, repo: repo2 } = resolveGithubRepo(
15450
+ cfg,
15451
+ env.GITHUB_REPO,
15452
+ gitDerivedSlug
15453
+ );
15454
+ const octokit = new Octokit2({ auth: githubToken });
15455
+ const issues = await octokit.paginate(
15456
+ octokit.rest.issues.listForRepo,
15457
+ {
15458
+ owner,
15459
+ repo: repo2,
15460
+ state: listState,
15461
+ per_page: 100
15462
+ }
15463
+ );
15464
+ const { candidates, skipped } = partitionGithubIssuesForImport({
15465
+ projection: proj,
15466
+ issues,
15467
+ onlyIssueNumbers
15468
+ });
15469
+ if (o.dryRun) {
15470
+ deps.log(
15471
+ formatOutput(g.format, {
15472
+ ok: true,
15473
+ dryRun: true,
15474
+ candidates,
15475
+ skipped
15476
+ })
15477
+ );
15478
+ } else {
15479
+ const imported = [];
15480
+ for (const c of candidates) {
15481
+ const ticketId = ulid();
15482
+ const createPayload = mergeTicketImportCreatePayload(
15483
+ ticketId,
15484
+ c.ticketCreatedPayloadBase,
15485
+ storyTrimmed
15486
+ );
15487
+ const createdEvt = makeEvent(
15488
+ "TicketCreated",
15489
+ createPayload,
15490
+ deps.clock,
15491
+ actor
15492
+ );
15493
+ await appendEventLine(
15494
+ session.worktreePath,
15495
+ createdEvt,
15496
+ deps.clock
15497
+ );
15498
+ const linkEvt = makeEvent(
15499
+ "GithubIssueLinked",
15500
+ { ticketId, issueNumber: c.issueNumber },
15501
+ deps.clock,
15502
+ actor
15503
+ );
15504
+ await appendEventLine(session.worktreePath, linkEvt, deps.clock);
15505
+ imported.push({ ticketId, issueNumber: c.issueNumber });
15506
+ }
15507
+ await commitDataWorktreeIfNeeded(
15508
+ session.worktreePath,
15509
+ formatDataBranchCommitMessage(
15510
+ "hyper-pm: import-github-issues",
15511
+ actor
15512
+ ),
15513
+ runGit
15514
+ );
15515
+ deps.log(
15516
+ formatOutput(g.format, {
15517
+ ok: true,
15518
+ imported,
15519
+ skipped
15520
+ })
15521
+ );
15522
+ }
15523
+ } catch (e) {
15524
+ deps.error(e instanceof Error ? e.message : String(e));
15525
+ deps.exit(ExitCode.ExternalApi);
15526
+ }
15527
+ } finally {
15528
+ await session.dispose();
15529
+ }
15530
+ deps.exit(ExitCode.Success);
15531
+ });
15221
15532
  program2.command("sync").description("GitHub Issues sync").option("--no-github", "skip network sync", false).action(async function() {
15222
15533
  const g = readGlobals(this);
15223
15534
  const o = this.opts();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyper-pm",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Git-backed work-item CLI with optional GitHub sync",
5
5
  "repository": {
6
6
  "type": "git",