@zhuk/spego 0.11.0 → 0.13.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 (64) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +28 -5
  3. package/dist/artifacts/engine.d.ts +4 -7
  4. package/dist/artifacts/engine.js +76 -23
  5. package/dist/artifacts/engine.js.map +1 -1
  6. package/dist/artifacts/schemas.js +19 -1
  7. package/dist/artifacts/schemas.js.map +1 -1
  8. package/dist/artifacts/storage.d.ts +3 -2
  9. package/dist/artifacts/storage.js +42 -7
  10. package/dist/artifacts/storage.js.map +1 -1
  11. package/dist/artifacts/types.d.ts +1 -1
  12. package/dist/artifacts/types.js +1 -0
  13. package/dist/artifacts/types.js.map +1 -1
  14. package/dist/artifacts/validation.js +1 -1
  15. package/dist/artifacts/validation.js.map +1 -1
  16. package/dist/cli/body-input.js +12 -2
  17. package/dist/cli/body-input.js.map +1 -1
  18. package/dist/cli/commands/artifact.js +81 -13
  19. package/dist/cli/commands/artifact.js.map +1 -1
  20. package/dist/cli/commands/meta.js +1 -1
  21. package/dist/cli/commands/meta.js.map +1 -1
  22. package/dist/cli/commands/mirror.d.ts +2 -0
  23. package/dist/cli/commands/mirror.js +253 -0
  24. package/dist/cli/commands/mirror.js.map +1 -0
  25. package/dist/cli/commands/view.js +29 -27
  26. package/dist/cli/commands/view.js.map +1 -1
  27. package/dist/cli/epic-slug.d.ts +2 -0
  28. package/dist/cli/epic-slug.js +17 -0
  29. package/dist/cli/epic-slug.js.map +1 -0
  30. package/dist/cli/runtime.d.ts +2 -0
  31. package/dist/cli/runtime.js +2 -1
  32. package/dist/cli/runtime.js.map +1 -1
  33. package/dist/cli.js +2 -0
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{commands → command-meta}/registry.js +11 -0
  36. package/dist/command-meta/registry.js.map +1 -0
  37. package/dist/delivery/mirror.d.ts +62 -0
  38. package/dist/delivery/mirror.js +466 -0
  39. package/dist/delivery/mirror.js.map +1 -0
  40. package/dist/errors.d.ts +1 -1
  41. package/dist/errors.js.map +1 -1
  42. package/dist/generator/claude.js +1 -1
  43. package/dist/generator/claude.js.map +1 -1
  44. package/dist/index/indexer.js +37 -8
  45. package/dist/index/indexer.js.map +1 -1
  46. package/dist/index/revisions.d.ts +2 -2
  47. package/dist/index/revisions.js +18 -3
  48. package/dist/index/revisions.js.map +1 -1
  49. package/dist/workflows/registry.js +71 -1
  50. package/dist/workflows/registry.js.map +1 -1
  51. package/dist/workspace/config.d.ts +0 -66
  52. package/dist/workspace/config.js +4 -13
  53. package/dist/workspace/config.js.map +1 -1
  54. package/dist/workspace/init.js +16 -2
  55. package/dist/workspace/init.js.map +1 -1
  56. package/dist/workspace/paths.d.ts +12 -0
  57. package/dist/workspace/paths.js +69 -0
  58. package/dist/workspace/paths.js.map +1 -1
  59. package/package.json +2 -4
  60. package/dist/commands/registry.js.map +0 -1
  61. package/dist/index.d.ts +0 -32
  62. package/dist/index.js +0 -20
  63. package/dist/index.js.map +0 -1
  64. /package/dist/{commands → command-meta}/registry.d.ts +0 -0
@@ -0,0 +1,17 @@
1
+ import { SpegoError } from '../errors.js';
2
+ export async function assertEpicSlugActive(adapter, slug) {
3
+ if (adapter === null) {
4
+ process.stderr.write(`warning: OpenSpec change link for epic "${slug}" was not verified\n`);
5
+ return;
6
+ }
7
+ try {
8
+ await adapter.getEpic(slug);
9
+ }
10
+ catch (err) {
11
+ throw new SpegoError('VALIDATION_FAILED', `OpenSpec change not found or archived for epic slug: ${slug}`, {
12
+ slug,
13
+ cause: err instanceof Error ? err.message : String(err),
14
+ });
15
+ }
16
+ }
17
+ //# sourceMappingURL=epic-slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epic-slug.js","sourceRoot":"","sources":["../../src/cli/epic-slug.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAA+B,EAC/B,IAAY;IAEZ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,IAAI,sBAAsB,CAAC,CAAC;QAC5F,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAAC,mBAAmB,EAAE,wDAAwD,IAAI,EAAE,EAAE;YACxG,IAAI;YACJ,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -22,5 +22,7 @@ export declare function runCommand<T>(ctx: RuntimeContext, fn: (json: boolean) =
22
22
  /** Run a command body that needs an `ArtifactEngine`; the engine is closed on every exit path. */
23
23
  export declare function runEngineCommand<T>(ctx: RuntimeContext & {
24
24
  cwd?: string;
25
+ jsonOverride?: boolean;
26
+ validate?: () => Promise<void> | void;
25
27
  }, fn: (engine: ArtifactEngine, json: boolean) => Promise<CommandResult<T>>): Promise<void>;
26
28
  export {};
@@ -26,9 +26,10 @@ export async function runCommand(ctx, fn) {
26
26
  }
27
27
  /** Run a command body that needs an `ArtifactEngine`; the engine is closed on every exit path. */
28
28
  export async function runEngineCommand(ctx, fn) {
29
- const json = getJsonMode(ctx.program);
29
+ const json = ctx.jsonOverride ?? getJsonMode(ctx.program);
30
30
  let engine;
31
31
  try {
32
+ await ctx.validate?.();
32
33
  engine = await ArtifactEngine.open({ projectRoot: ctx.cwd });
33
34
  }
34
35
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/cli/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAWnC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,IAAe,CAAC;AACxC,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAmB,EACnB,EAAgD;IAEhD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAsC,EACtC,EAAwE;IAExE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/cli/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAWnC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC3C,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,IAAe,CAAC;AACvC,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,GAAmB,EACnB,EAAgD;IAEhD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC;QACJ,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjB,CAAC;AACF,CAAC;AAED,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,GAAqG,EACrG,EAAwE;IAExE,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,MAAM,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;QACvB,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjB,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACF,CAAC"}
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@ import { registerView } from './cli/commands/view.js';
19
19
  import { registerIndex } from './cli/commands/index-rebuild.js';
20
20
  import { registerSkills } from './cli/commands/skills.js';
21
21
  import { registerDelivery } from './cli/commands/delivery.js';
22
+ import { registerMirror } from './cli/commands/mirror.js';
22
23
  export function buildProgram() {
23
24
  const program = new Command();
24
25
  program
@@ -49,6 +50,7 @@ export function buildProgram() {
49
50
  registerIndex(program);
50
51
  registerSkills(program);
51
52
  registerDelivery(program);
53
+ registerMirror(program);
52
54
  return program;
53
55
  }
54
56
  /**
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -140,6 +140,17 @@ export const COMMAND_REGISTRY = [
140
140
  inputSchema: {},
141
141
  outputModes: ['markdown', 'json'],
142
142
  },
143
+ {
144
+ name: 'mirror',
145
+ description: 'Show delivery mirror board',
146
+ slashName: '/spego:mirror',
147
+ category: 'planning',
148
+ inputSchema: {
149
+ graph: { name: 'graph', type: 'boolean', required: false, description: 'Show dependency graph' },
150
+ gaps: { name: 'gaps', type: 'boolean', required: false, description: 'Show gaps, missing artifacts, and blockers' },
151
+ },
152
+ outputModes: ['markdown', 'json'],
153
+ },
143
154
  ];
144
155
  export function getCommandByName(name) {
145
156
  return COMMAND_REGISTRY.find((c) => c.name === name);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/command-meta/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { DeliveryStatus } from './types.js';
2
+ export type WarningCode = 'dangling-dep' | 'dep-cycle' | 'ungroomed-change' | 'orphan-epic' | 'archived-in-sprint' | 'closable-sprint' | 'adapter-warning' | 'adapter-unavailable';
3
+ export interface MirrorWarning {
4
+ code: WarningCode;
5
+ message: string;
6
+ details?: Record<string, unknown>;
7
+ }
8
+ export interface MirrorArtifact {
9
+ id: string;
10
+ type: string;
11
+ slug: string;
12
+ title: string;
13
+ meta: Record<string, unknown>;
14
+ }
15
+ export interface MirrorSourceChange {
16
+ slug: string;
17
+ title: string;
18
+ status: DeliveryStatus;
19
+ archived?: boolean;
20
+ warnings?: string[];
21
+ }
22
+ export interface MirrorInput {
23
+ changes: MirrorSourceChange[];
24
+ epics: MirrorArtifact[];
25
+ sprints: MirrorArtifact[];
26
+ linkedArtifacts?: MirrorArtifact[];
27
+ warnings?: MirrorWarning[];
28
+ }
29
+ export interface MirrorGap {
30
+ flag: string;
31
+ note?: string;
32
+ }
33
+ export interface MirrorChange {
34
+ slug: string;
35
+ title: string;
36
+ status: DeliveryStatus;
37
+ blockers: string[];
38
+ gaps: MirrorGap[];
39
+ missing: string[];
40
+ warnings: WarningCode[];
41
+ }
42
+ export interface MirrorSprint {
43
+ slug: string;
44
+ title: string;
45
+ status: string;
46
+ startDate: string | null;
47
+ endDate: string | null;
48
+ changes: MirrorChange[];
49
+ }
50
+ export interface MirrorNext {
51
+ change: string;
52
+ sprint: string;
53
+ reason: string;
54
+ }
55
+ export interface MirrorBoard {
56
+ sprints: MirrorSprint[];
57
+ ungrouped: MirrorChange[];
58
+ warnings: MirrorWarning[];
59
+ next: MirrorNext | null;
60
+ }
61
+ export declare function deriveMirror(input: MirrorInput): MirrorBoard;
62
+ export declare function filterMirrorGaps(board: MirrorBoard): MirrorBoard;
@@ -0,0 +1,466 @@
1
+ const ISO_DATE_PREFIX = /^\d{4}-\d{2}-\d{2}(T.*)?$/;
2
+ const STATUS_VALUES = {
3
+ active: true,
4
+ completed: true,
5
+ 'planning-incomplete': true,
6
+ unknown: true,
7
+ };
8
+ const WARNING_ORDER = {
9
+ 'adapter-unavailable': 0,
10
+ 'adapter-warning': 1,
11
+ 'archived-in-sprint': 2,
12
+ 'closable-sprint': 3,
13
+ 'dangling-dep': 4,
14
+ 'dep-cycle': 5,
15
+ 'orphan-epic': 6,
16
+ 'ungroomed-change': 7,
17
+ };
18
+ export function deriveMirror(input) {
19
+ const sortedChanges = [...input.changes].sort((a, b) => a.slug.localeCompare(b.slug));
20
+ const sortedEpics = sortArtifacts(input.epics);
21
+ const sortedSprintArtifacts = sortArtifacts(input.sprints);
22
+ const linkedById = new Map((input.linkedArtifacts ?? []).map((artifact) => [artifact.id, artifact]));
23
+ const inputChangeBySlug = new Map(sortedChanges.map((item) => [item.slug, item]));
24
+ const epicsBySlug = new Map(sortedEpics.map((item) => [item.slug, item]));
25
+ const changeStates = new Map();
26
+ for (const item of sortedChanges) {
27
+ changeStates.set(item.slug, {
28
+ slug: item.slug,
29
+ title: item.title,
30
+ status: item.archived ? 'completed' : item.status,
31
+ archived: item.archived === true,
32
+ });
33
+ }
34
+ for (const epic of sortedEpics) {
35
+ const current = changeStates.get(epic.slug);
36
+ if (current) {
37
+ current.title = epic.title;
38
+ current.epic = epic;
39
+ continue;
40
+ }
41
+ changeStates.set(epic.slug, {
42
+ slug: epic.slug,
43
+ title: epic.title,
44
+ status: statusFromMeta(epic.meta),
45
+ archived: false,
46
+ epic,
47
+ });
48
+ }
49
+ const sprints = sortedSprintArtifacts.map((artifact) => toSprintPlan(artifact));
50
+ sprints.sort(compareSprints);
51
+ sprints.forEach((sprint, index) => {
52
+ sprint.order = index;
53
+ });
54
+ const scheduled = new Set();
55
+ const scheduleBySlug = new Map();
56
+ for (const sprint of sprints) {
57
+ for (const slug of sprint.changes) {
58
+ scheduled.add(slug);
59
+ const current = scheduleBySlug.get(slug);
60
+ if (current === undefined || sprint.order < current)
61
+ scheduleBySlug.set(slug, sprint.order);
62
+ if (!changeStates.has(slug)) {
63
+ const discovered = inputChangeBySlug.get(slug);
64
+ changeStates.set(slug, {
65
+ slug,
66
+ title: discovered?.title ?? slug,
67
+ status: discovered?.archived ? 'completed' : discovered?.status ?? 'unknown',
68
+ archived: discovered?.archived === true,
69
+ epic: epicsBySlug.get(slug),
70
+ });
71
+ }
72
+ }
73
+ }
74
+ const knownSlugs = new Set(changeStates.keys());
75
+ const depsBySlug = new Map();
76
+ for (const [slug, state] of [...changeStates].sort((a, b) => a[0].localeCompare(b[0]))) {
77
+ depsBySlug.set(slug, uniqueStrings(state.epic?.meta.deps));
78
+ }
79
+ const cycleMembers = findCycleMembers([...knownSlugs].sort(), depsBySlug, knownSlugs);
80
+ const warnings = [...(input.warnings ?? [])];
81
+ for (const source of sortedChanges) {
82
+ for (const warning of source.warnings ?? []) {
83
+ warnings.push({
84
+ code: 'adapter-warning',
85
+ message: `${source.slug}: ${warning}`,
86
+ details: { change: source.slug, warning },
87
+ });
88
+ }
89
+ }
90
+ for (const source of sortedChanges) {
91
+ if (source.archived)
92
+ continue;
93
+ if (epicsBySlug.has(source.slug))
94
+ continue;
95
+ warnings.push({
96
+ code: 'ungroomed-change',
97
+ message: `Active change "${source.slug}" has no epic artifact.`,
98
+ details: { change: source.slug },
99
+ });
100
+ }
101
+ for (const epic of sortedEpics) {
102
+ const source = inputChangeBySlug.get(epic.slug);
103
+ if (!source) {
104
+ warnings.push({
105
+ code: 'orphan-epic',
106
+ message: `Epic "${epic.slug}" does not resolve to an OpenSpec change.`,
107
+ details: { change: epic.slug, reason: 'missing' },
108
+ });
109
+ continue;
110
+ }
111
+ if (source.archived) {
112
+ warnings.push({
113
+ code: 'orphan-epic',
114
+ message: `Epic "${epic.slug}" points at an archived OpenSpec change.`,
115
+ details: { change: epic.slug, reason: 'archived' },
116
+ });
117
+ }
118
+ }
119
+ for (const sprint of sprints) {
120
+ for (const slug of sprint.changes) {
121
+ if (!inputChangeBySlug.get(slug)?.archived)
122
+ continue;
123
+ warnings.push({
124
+ code: 'archived-in-sprint',
125
+ message: `Sprint "${sprint.artifact.slug}" includes archived change "${slug}".`,
126
+ details: { sprint: sprint.artifact.slug, change: slug },
127
+ });
128
+ }
129
+ }
130
+ for (const [slug, deps] of [...depsBySlug].sort((a, b) => a[0].localeCompare(b[0]))) {
131
+ for (const dep of deps) {
132
+ if (knownSlugs.has(dep))
133
+ continue;
134
+ warnings.push({
135
+ code: 'dangling-dep',
136
+ message: `Change "${slug}" depends on unknown change "${dep}".`,
137
+ details: { change: slug, dep },
138
+ });
139
+ }
140
+ }
141
+ for (const slug of [...cycleMembers].sort()) {
142
+ warnings.push({
143
+ code: 'dep-cycle',
144
+ message: `Change "${slug}" is part of a dependency cycle.`,
145
+ details: { change: slug, cycle: [...cycleMembers].sort() },
146
+ });
147
+ }
148
+ const cycleReach = new Map();
149
+ const blockersBySlug = new Map();
150
+ for (const slug of [...knownSlugs].sort()) {
151
+ blockersBySlug.set(slug, blockersFor(slug, depsBySlug, knownSlugs, changeStates, scheduleBySlug, cycleMembers, cycleReach));
152
+ }
153
+ const sortedWarnings = sortWarnings(warnings);
154
+ const warningCodesByChange = new Map();
155
+ for (const warning of sortedWarnings) {
156
+ const change = typeof warning.details?.change === 'string' ? warning.details.change : undefined;
157
+ if (!change)
158
+ continue;
159
+ const codes = warningCodesByChange.get(change) ?? [];
160
+ codes.push(warning.code);
161
+ warningCodesByChange.set(change, codes);
162
+ }
163
+ const missingBySlug = new Map();
164
+ const gapsBySlug = new Map();
165
+ for (const [slug, state] of [...changeStates].sort((a, b) => a[0].localeCompare(b[0]))) {
166
+ const epic = state.epic;
167
+ if (!epic) {
168
+ missingBySlug.set(slug, []);
169
+ gapsBySlug.set(slug, []);
170
+ continue;
171
+ }
172
+ const links = uniqueStrings(epic.meta.links);
173
+ const coveredTypes = new Set();
174
+ for (const id of links) {
175
+ const linked = linkedById.get(id);
176
+ if (linked)
177
+ coveredTypes.add(linked.type);
178
+ }
179
+ missingBySlug.set(slug, uniqueStrings(epic.meta.requires).filter((type) => !coveredTypes.has(type)).sort());
180
+ gapsBySlug.set(slug, gapsFromMeta(epic.meta.gaps));
181
+ }
182
+ const toMirrorChange = (slug) => {
183
+ const state = changeStates.get(slug);
184
+ return {
185
+ slug,
186
+ title: state?.title ?? slug,
187
+ status: state?.status ?? 'unknown',
188
+ blockers: blockersBySlug.get(slug) ?? [],
189
+ gaps: gapsBySlug.get(slug) ?? [],
190
+ missing: missingBySlug.get(slug) ?? [],
191
+ warnings: warningCodesByChange.get(slug) ?? [],
192
+ };
193
+ };
194
+ const sprintRows = sprints.map((sprint) => ({
195
+ slug: sprint.artifact.slug,
196
+ title: sprint.artifact.title,
197
+ status: sprint.status,
198
+ startDate: sprint.startDate,
199
+ endDate: sprint.endDate,
200
+ changes: sprint.changes.map((slug) => toMirrorChange(slug)),
201
+ }));
202
+ for (const sprint of sprintRows) {
203
+ if (sprint.status === 'closed')
204
+ continue;
205
+ if (sprint.changes.length === 0)
206
+ continue;
207
+ if (!sprint.changes.every((item) => item.status === 'completed'))
208
+ continue;
209
+ sortedWarnings.push({
210
+ code: 'closable-sprint',
211
+ message: `Sprint "${sprint.slug}" has no pending changes and can be closed.`,
212
+ details: { sprint: sprint.slug },
213
+ });
214
+ }
215
+ const finalWarnings = sortWarnings(sortedWarnings);
216
+ const finalWarningCodesByChange = new Map();
217
+ for (const warning of finalWarnings) {
218
+ const change = typeof warning.details?.change === 'string' ? warning.details.change : undefined;
219
+ if (!change)
220
+ continue;
221
+ const codes = finalWarningCodesByChange.get(change) ?? [];
222
+ codes.push(warning.code);
223
+ finalWarningCodesByChange.set(change, codes);
224
+ }
225
+ for (const sprint of sprintRows) {
226
+ sprint.changes = sprint.changes.map((item) => ({
227
+ ...item,
228
+ warnings: finalWarningCodesByChange.get(item.slug) ?? item.warnings,
229
+ }));
230
+ }
231
+ const ungrouped = [...changeStates.keys()]
232
+ .filter((slug) => !scheduled.has(slug))
233
+ .sort()
234
+ .map((slug) => {
235
+ const item = toMirrorChange(slug);
236
+ return { ...item, warnings: finalWarningCodesByChange.get(slug) ?? item.warnings };
237
+ });
238
+ return {
239
+ sprints: sprintRows,
240
+ ungrouped,
241
+ warnings: finalWarnings,
242
+ next: chooseNext(sprintRows),
243
+ };
244
+ }
245
+ export function filterMirrorGaps(board) {
246
+ return {
247
+ sprints: board.sprints.map((sprint) => ({
248
+ ...sprint,
249
+ changes: sprint.changes.filter(hasGapSignal),
250
+ })),
251
+ ungrouped: board.ungrouped.filter(hasGapSignal),
252
+ warnings: board.warnings,
253
+ next: board.next,
254
+ };
255
+ }
256
+ function sortArtifacts(artifacts) {
257
+ const copy = [...artifacts];
258
+ copy.sort((a, b) => {
259
+ const bySlug = a.slug.localeCompare(b.slug);
260
+ if (bySlug !== 0)
261
+ return bySlug;
262
+ return a.id.localeCompare(b.id);
263
+ });
264
+ return copy;
265
+ }
266
+ function statusFromMeta(meta) {
267
+ if (typeof meta.status === 'string' && STATUS_VALUES[meta.status]) {
268
+ return meta.status;
269
+ }
270
+ return 'unknown';
271
+ }
272
+ function toSprintPlan(artifact) {
273
+ const start = normalizeDateField(artifact.meta.startDate);
274
+ return {
275
+ artifact,
276
+ order: 0,
277
+ parsedStart: start.sortKey,
278
+ changes: uniqueStrings(artifact.meta.changes),
279
+ status: typeof artifact.meta.status === 'string' ? artifact.meta.status : 'planned',
280
+ startDate: start.value,
281
+ endDate: normalizeDateField(artifact.meta.endDate).value,
282
+ };
283
+ }
284
+ function normalizeDateField(value) {
285
+ if (typeof value !== 'string')
286
+ return { sortKey: Number.POSITIVE_INFINITY, value: null };
287
+ const trimmed = value.trim();
288
+ if (trimmed.length === 0)
289
+ return { sortKey: Number.POSITIVE_INFINITY, value: null };
290
+ if (!ISO_DATE_PREFIX.test(trimmed))
291
+ return { sortKey: Number.POSITIVE_INFINITY, value: trimmed };
292
+ const time = new Date(trimmed).getTime();
293
+ if (!Number.isFinite(time))
294
+ return { sortKey: Number.POSITIVE_INFINITY, value: trimmed };
295
+ return { sortKey: time, value: trimmed };
296
+ }
297
+ function compareSprints(a, b) {
298
+ const byDate = a.parsedStart - b.parsedStart;
299
+ if (!Number.isNaN(byDate) && byDate !== 0)
300
+ return byDate;
301
+ return a.artifact.slug.localeCompare(b.artifact.slug);
302
+ }
303
+ function uniqueStrings(value) {
304
+ if (!Array.isArray(value))
305
+ return [];
306
+ const out = [];
307
+ const seen = new Set();
308
+ for (const item of value) {
309
+ if (typeof item !== 'string')
310
+ continue;
311
+ if (seen.has(item))
312
+ continue;
313
+ seen.add(item);
314
+ out.push(item);
315
+ }
316
+ return out;
317
+ }
318
+ function gapsFromMeta(value) {
319
+ if (!Array.isArray(value))
320
+ return [];
321
+ const gaps = [];
322
+ for (const item of value) {
323
+ if (typeof item !== 'object' || item === null)
324
+ continue;
325
+ const record = item;
326
+ if (typeof record.flag !== 'string' || record.flag.length === 0)
327
+ continue;
328
+ const gap = { flag: record.flag };
329
+ if (typeof record.note === 'string')
330
+ gap.note = record.note;
331
+ gaps.push(gap);
332
+ }
333
+ return gaps;
334
+ }
335
+ function findCycleMembers(slugs, depsBySlug, knownSlugs) {
336
+ const state = new Map();
337
+ const stack = [];
338
+ const stackIndex = new Map();
339
+ const members = new Set();
340
+ let steps = 0;
341
+ const maxSteps = Math.max(100, slugs.length * slugs.length + slugs.length);
342
+ const visit = (slug) => {
343
+ if (state.get(slug) === 'done')
344
+ return;
345
+ if (steps > maxSteps) {
346
+ for (const item of stack)
347
+ members.add(item);
348
+ return;
349
+ }
350
+ steps += 1;
351
+ state.set(slug, 'visiting');
352
+ stackIndex.set(slug, stack.length);
353
+ stack.push(slug);
354
+ for (const dep of [...(depsBySlug.get(slug) ?? [])].sort()) {
355
+ if (!knownSlugs.has(dep))
356
+ continue;
357
+ const depState = state.get(dep);
358
+ if (depState === 'visiting') {
359
+ const start = stackIndex.get(dep) ?? 0;
360
+ for (const item of stack.slice(start))
361
+ members.add(item);
362
+ continue;
363
+ }
364
+ visit(dep);
365
+ }
366
+ stack.pop();
367
+ stackIndex.delete(slug);
368
+ state.set(slug, 'done');
369
+ };
370
+ for (const slug of slugs)
371
+ visit(slug);
372
+ return members;
373
+ }
374
+ function blockersFor(slug, depsBySlug, knownSlugs, states, scheduleBySlug, cycleMembers, cycleReach) {
375
+ const blockers = new Set();
376
+ if (cycleMembers.has(slug))
377
+ blockers.add('dep-cycle');
378
+ const currentSchedule = scheduleBySlug.get(slug);
379
+ for (const dep of depsBySlug.get(slug) ?? []) {
380
+ if (!knownSlugs.has(dep)) {
381
+ blockers.add(dep);
382
+ continue;
383
+ }
384
+ if (leadsToCycle(dep, depsBySlug, knownSlugs, cycleMembers, cycleReach, new Set())) {
385
+ blockers.add(dep);
386
+ continue;
387
+ }
388
+ if (states.get(dep)?.status === 'completed')
389
+ continue;
390
+ const depSchedule = scheduleBySlug.get(dep);
391
+ if (currentSchedule !== undefined && depSchedule !== undefined && depSchedule <= currentSchedule)
392
+ continue;
393
+ blockers.add(dep);
394
+ }
395
+ return [...blockers].sort();
396
+ }
397
+ function leadsToCycle(slug, depsBySlug, knownSlugs, cycleMembers, memo, seen) {
398
+ const cached = memo.get(slug);
399
+ if (cached !== undefined)
400
+ return cached;
401
+ if (cycleMembers.has(slug)) {
402
+ memo.set(slug, true);
403
+ return true;
404
+ }
405
+ if (seen.has(slug))
406
+ return false;
407
+ seen.add(slug);
408
+ for (const dep of depsBySlug.get(slug) ?? []) {
409
+ if (!knownSlugs.has(dep))
410
+ continue;
411
+ if (leadsToCycle(dep, depsBySlug, knownSlugs, cycleMembers, memo, seen)) {
412
+ memo.set(slug, true);
413
+ seen.delete(slug);
414
+ return true;
415
+ }
416
+ }
417
+ seen.delete(slug);
418
+ memo.set(slug, false);
419
+ return false;
420
+ }
421
+ function sortWarnings(warnings) {
422
+ const copy = [...warnings];
423
+ copy.sort((a, b) => {
424
+ const byCode = WARNING_ORDER[a.code] - WARNING_ORDER[b.code];
425
+ if (byCode !== 0)
426
+ return byCode;
427
+ const aDetail = warningSortKey(a);
428
+ const bDetail = warningSortKey(b);
429
+ return aDetail.localeCompare(bDetail);
430
+ });
431
+ return copy;
432
+ }
433
+ function warningSortKey(warning) {
434
+ const details = warning.details ?? {};
435
+ const change = typeof details.change === 'string' ? details.change : '';
436
+ const sprint = typeof details.sprint === 'string' ? details.sprint : '';
437
+ const dep = typeof details.dep === 'string' ? details.dep : '';
438
+ return `${change}\u0000${sprint}\u0000${dep}\u0000${warning.message}`;
439
+ }
440
+ function chooseNext(sprints) {
441
+ const active = sprints.filter((sprint) => sprint.status === 'active');
442
+ const candidates = active.length > 0 ? active : sprints.filter((sprint) => sprint.status === 'planned');
443
+ const source = active.length > 0 ? 'active sprint' : 'planned sprint';
444
+ for (const sprint of candidates) {
445
+ for (const change of sprint.changes) {
446
+ if (change.status === 'completed')
447
+ continue;
448
+ if (change.blockers.length > 0)
449
+ continue;
450
+ return {
451
+ change: change.slug,
452
+ sprint: sprint.slug,
453
+ reason: `first pending unblocked change in ${source}`,
454
+ };
455
+ }
456
+ }
457
+ return null;
458
+ }
459
+ function hasGapSignal(change) {
460
+ if (change.gaps.length > 0)
461
+ return true;
462
+ if (change.missing.length > 0)
463
+ return true;
464
+ return change.blockers.length > 0;
465
+ }
466
+ //# sourceMappingURL=mirror.js.map