avtc-pi-featyard 1.1.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 (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +194 -0
  3. package/agents/fy-design-reviewer.md +77 -0
  4. package/agents/fy-feature-verifier.md +24 -0
  5. package/agents/fy-general-reviewer.md +81 -0
  6. package/agents/fy-guidelines-reviewer.md +46 -0
  7. package/agents/fy-implementer.md +20 -0
  8. package/agents/fy-performance-reviewer.md +46 -0
  9. package/agents/fy-plan-reviewer.md +74 -0
  10. package/agents/fy-plan-verifier.md +44 -0
  11. package/agents/fy-quality-reviewer.md +50 -0
  12. package/agents/fy-requirements-reviewer.md +46 -0
  13. package/agents/fy-researcher.md +6 -0
  14. package/agents/fy-security-reviewer.md +46 -0
  15. package/agents/fy-task-verifier.md +42 -0
  16. package/agents/fy-testing-reviewer.md +60 -0
  17. package/assets/images/kanban-board.png +0 -0
  18. package/assets/images/settings-dropdown.png +0 -0
  19. package/assets/images/settings.png +0 -0
  20. package/assets/images/widget.png +0 -0
  21. package/docs/CONFIGURATION.md +266 -0
  22. package/index.ts +8 -0
  23. package/package.json +85 -0
  24. package/skills/fy-design/SKILL.md +108 -0
  25. package/skills/fy-design-review/SKILL.md +45 -0
  26. package/skills/fy-finish/SKILL.md +28 -0
  27. package/skills/fy-implement/SKILL.md +36 -0
  28. package/skills/fy-plan/SKILL.md +77 -0
  29. package/skills/fy-plan-review/SKILL.md +45 -0
  30. package/skills/fy-research/SKILL.md +38 -0
  31. package/skills/fy-review/SKILL.md +88 -0
  32. package/skills/fy-verify/SKILL.md +23 -0
  33. package/src/commands/auto-agent-commands.ts +748 -0
  34. package/src/commands/kanban-commands.ts +278 -0
  35. package/src/commands/workflow-commands.ts +531 -0
  36. package/src/compaction/compact-handler.ts +371 -0
  37. package/src/compaction/compact-message.ts +52 -0
  38. package/src/compaction/compact-trigger.ts +121 -0
  39. package/src/compaction/safe-editor-write.ts +18 -0
  40. package/src/events/agent/agent-end.ts +19 -0
  41. package/src/events/agent/agent-settled.ts +20 -0
  42. package/src/events/agent/agent-start.ts +18 -0
  43. package/src/events/agent/turn-end.ts +20 -0
  44. package/src/events/agent/turn-start.ts +19 -0
  45. package/src/events/event-deps.ts +33 -0
  46. package/src/events/index.ts +91 -0
  47. package/src/events/input/input.ts +143 -0
  48. package/src/events/session/context.ts +68 -0
  49. package/src/events/session/model-select.ts +20 -0
  50. package/src/events/session/session-compact.ts +20 -0
  51. package/src/events/session/session-shutdown.ts +26 -0
  52. package/src/events/session/session-start.ts +45 -0
  53. package/src/events/session/session-tree.ts +17 -0
  54. package/src/events/tool/tool-call.ts +58 -0
  55. package/src/events/tool/tool-result.ts +54 -0
  56. package/src/git/git-queries.ts +154 -0
  57. package/src/git/resolve-base-branch.ts +173 -0
  58. package/src/git/worktrees/worktree-helpers.ts +142 -0
  59. package/src/git/worktrees/worktree-interception.ts +66 -0
  60. package/src/git/worktrees/worktree-lifecycle.ts +380 -0
  61. package/src/guardrails/completion-gating.ts +63 -0
  62. package/src/guardrails/file-classifier.ts +461 -0
  63. package/src/guardrails/force-add-guard.ts +312 -0
  64. package/src/guardrails/guardrail-tracker.ts +73 -0
  65. package/src/guardrails/guardrail-violations.ts +47 -0
  66. package/src/guardrails/guardrails-engine.ts +663 -0
  67. package/src/guardrails/session-guardrails.ts +75 -0
  68. package/src/guardrails/shell-decompose.ts +285 -0
  69. package/src/guardrails/tdd-enforcement.ts +122 -0
  70. package/src/guardrails/test-output.ts +272 -0
  71. package/src/index.ts +598 -0
  72. package/src/integrations/parallel-work-guardrail-integration.ts +29 -0
  73. package/src/integrations/subagent-integration.ts +166 -0
  74. package/src/integrations/todo-integration.ts +58 -0
  75. package/src/kanban/auto-agent/auto-agent-cleanup.ts +37 -0
  76. package/src/kanban/auto-agent/auto-agent-events.ts +142 -0
  77. package/src/kanban/auto-agent/auto-agent-grace-period.ts +83 -0
  78. package/src/kanban/auto-agent/auto-agent-lifecycle.ts +497 -0
  79. package/src/kanban/auto-agent/auto-agent-notify.ts +43 -0
  80. package/src/kanban/auto-agent/auto-agent-pause.ts +44 -0
  81. package/src/kanban/auto-agent/auto-agent-state-machine.ts +629 -0
  82. package/src/kanban/data/kanban-database.ts +851 -0
  83. package/src/kanban/data/kanban-db-schema.ts +137 -0
  84. package/src/kanban/data/kanban-detect-project.ts +43 -0
  85. package/src/kanban/data/kanban-move-feature.ts +43 -0
  86. package/src/kanban/data/kanban-types.ts +64 -0
  87. package/src/kanban/ensure-feature.ts +226 -0
  88. package/src/kanban/kanban-board-ui/app.js +1125 -0
  89. package/src/kanban/kanban-board-ui/csv-parser.d.ts +5 -0
  90. package/src/kanban/kanban-board-ui/csv-parser.js +80 -0
  91. package/src/kanban/kanban-board-ui/index.html +127 -0
  92. package/src/kanban/kanban-board-ui/style.css +820 -0
  93. package/src/kanban/kanban-bridge.ts +216 -0
  94. package/src/kanban/kanban-context.ts +43 -0
  95. package/src/kanban/kanban-events.ts +34 -0
  96. package/src/kanban/kanban-generate-title.ts +259 -0
  97. package/src/kanban/kanban-generate-topic.ts +170 -0
  98. package/src/kanban/kanban-operations.ts +170 -0
  99. package/src/kanban/kanban-server.ts +931 -0
  100. package/src/kanban/kanban-turn-handlers.ts +55 -0
  101. package/src/kanban/model-capture.ts +34 -0
  102. package/src/log.ts +47 -0
  103. package/src/phases/env-sync.ts +64 -0
  104. package/src/phases/execution-mode.ts +79 -0
  105. package/src/phases/implement-to-verify.ts +31 -0
  106. package/src/phases/phase-progression.ts +249 -0
  107. package/src/phases/phase-transitions.ts +226 -0
  108. package/src/phases/workflow-router.ts +98 -0
  109. package/src/prompts/finish-instructions.ts +143 -0
  110. package/src/prompts/skill-block-builder.ts +73 -0
  111. package/src/prompts/skill-expansion.ts +262 -0
  112. package/src/prompts/task-gate-skill.ts +83 -0
  113. package/src/prompts/template-engine.ts +433 -0
  114. package/src/prompts/text-blocks.ts +279 -0
  115. package/src/review/review-context.ts +56 -0
  116. package/src/review/review-counter.ts +66 -0
  117. package/src/review/review-empty-loop-tracking.ts +52 -0
  118. package/src/review/review-loops.ts +217 -0
  119. package/src/review/review-report.ts +101 -0
  120. package/src/settings/model-overrides.ts +269 -0
  121. package/src/settings/settings-schema.ts +458 -0
  122. package/src/settings/settings-types.ts +81 -0
  123. package/src/settings/settings-ui.ts +222 -0
  124. package/src/shared/errors.ts +24 -0
  125. package/src/shared/global.d.ts +22 -0
  126. package/src/shared/session-entries.ts +24 -0
  127. package/src/shared/types.ts +195 -0
  128. package/src/shared/workflow-refs.ts +253 -0
  129. package/src/shared/workflow-types.ts +180 -0
  130. package/src/snippets/vendored/subscribe-to-dialog-coordinator.ts +98 -0
  131. package/src/snippets/vendored/subscribe-to-notifications.ts +150 -0
  132. package/src/snippets/vendored/subscribe-to-parallel-work-guardrail.ts +35 -0
  133. package/src/snippets/vendored/subscribe-to-subagent.ts +67 -0
  134. package/src/snippets/vendored/subscribe-to-todo.ts +63 -0
  135. package/src/state/archive-artifacts.ts +602 -0
  136. package/src/state/artifact-junction.ts +462 -0
  137. package/src/state/artifact-paths.ts +230 -0
  138. package/src/state/feature-activation.ts +239 -0
  139. package/src/state/feature-management.ts +325 -0
  140. package/src/state/feature-record-store.ts +39 -0
  141. package/src/state/feature-session.ts +263 -0
  142. package/src/state/feature-state.ts +439 -0
  143. package/src/state/post-turn-dispatch.ts +106 -0
  144. package/src/state/session-lifecycle.ts +346 -0
  145. package/src/state/state-persistence.ts +122 -0
  146. package/src/state/worth-notes.ts +89 -0
  147. package/src/tools/add-to-backlog.ts +117 -0
  148. package/src/tools/phase-ready.ts +742 -0
  149. package/src/tools/task-ready-advance.ts +235 -0
  150. package/src/tools/text-result.ts +15 -0
  151. package/src/ui/featyard-widget.ts +250 -0
  152. package/src/ui/manage-features-dialog.ts +246 -0
  153. package/src/ui/select-dialog.ts +27 -0
@@ -0,0 +1,246 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
3
+
4
+ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
5
+ import { type Component, Key, matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
6
+ import { withCoordinator } from "../snippets/vendored/subscribe-to-dialog-coordinator.js";
7
+ import type { FeatureState } from "../state/feature-state.js";
8
+
9
+ // Minimal TUI interface for testing
10
+ export interface TUILike {
11
+ requestRender(): void;
12
+ }
13
+
14
+ // Raw terminal escape sequences for handleInput()
15
+ const INPUT = {
16
+ up: "\x1b[A",
17
+ down: "\x1b[B",
18
+ enter: "\r",
19
+ escape: "\x1b",
20
+ space: " ",
21
+ } as const;
22
+
23
+ /**
24
+ * Formats feature info for display: the current workflow phase.
25
+ * (Per-task counts are owned by the TODO widget; the manage-features dialog shows phase only.)
26
+ */
27
+ export function formatFeatureInfo(feature: FeatureState): string {
28
+ return feature.workflow?.currentPhase ?? "unknown";
29
+ }
30
+
31
+ export interface ManageFeaturesResult {
32
+ action: "mark_completed" | "delete";
33
+ slugs: string[];
34
+ }
35
+
36
+ /** Sentinel for the done() callback — dialog cancelled via Escape (no result action). */
37
+ const NO_MANAGE_RESULT: ManageFeaturesResult | null = null;
38
+
39
+ /**
40
+ * Custom TUI component for managing feature state files.
41
+ * Shows a multi-select list of features with Mark completed / Delete actions.
42
+ */
43
+ export class ManageFeaturesComponent implements Component {
44
+ private features: FeatureState[];
45
+ private tui: TUILike;
46
+ private theme: Theme;
47
+ private done: (result: ManageFeaturesResult | null) => void;
48
+
49
+ private selectedIndices: Set<number> = new Set();
50
+ private cursorIndex: number = 0;
51
+ private _resolved: boolean = false;
52
+
53
+ // Render cache
54
+ private cachedWidth?: number;
55
+ private cachedLines?: string[];
56
+
57
+ constructor(
58
+ features: FeatureState[],
59
+ tui: TUILike,
60
+ theme: Theme,
61
+ done: (result: ManageFeaturesResult | null) => void,
62
+ ) {
63
+ this.features = features;
64
+ this.tui = tui;
65
+ this.theme = theme;
66
+ this.done = done;
67
+ }
68
+
69
+ // --- Public interface ---
70
+
71
+ invalidate(): void {
72
+ this.cachedWidth = undefined;
73
+ this.cachedLines = undefined;
74
+ }
75
+
76
+ render(width: number): string[] {
77
+ if (this.cachedWidth === width && this.cachedLines) {
78
+ return this.cachedLines;
79
+ }
80
+
81
+ const t = this.theme;
82
+ const lines: string[] = [];
83
+ const add = (s: string) => lines.push(truncateToWidth(s, width));
84
+
85
+ // Header
86
+ add(t.fg("accent", "─".repeat(width)));
87
+ add(t.fg("text", " Manage state files"));
88
+ add("");
89
+
90
+ // Feature checkboxes
91
+ for (let i = 0; i < this.features.length; i++) {
92
+ const f = this.features[i];
93
+ const isCursor = i === this.cursorIndex;
94
+ const isChecked = this.selectedIndices.has(i);
95
+ const prefix = isCursor ? t.fg("accent", ">") : " ";
96
+ const box = isChecked ? t.fg("accent", "[✓]") : t.fg("dim", "[ ]");
97
+ const info = formatFeatureInfo(f);
98
+ const label = `${f.featureSlug} — ${info}`;
99
+ const labelColor = isCursor ? "accent" : "text";
100
+ add(`${prefix} ${box} ${t.fg(labelColor, label)}`);
101
+ }
102
+
103
+ add("");
104
+
105
+ // Toggle button
106
+ const allChecked = this.features.length > 0 && this.selectedIndices.size === this.features.length;
107
+ const toggleLabel = allChecked ? "Deselect All" : "Select All";
108
+ const toggleIdx = this.features.length;
109
+ const isToggleCursor = this.cursorIndex === toggleIdx;
110
+ const togglePrefix = isToggleCursor ? t.fg("accent", ">") : " ";
111
+ const toggleStyle = isToggleCursor ? "accent" : "text";
112
+ add(`${togglePrefix} ${t.fg(toggleStyle, `[${toggleLabel}]`)}`);
113
+
114
+ add("");
115
+
116
+ // Action buttons
117
+ const markCompletedIdx = this.features.length + 1;
118
+ const deleteIdx = this.features.length + 2;
119
+ const isMarkCursor = this.cursorIndex === markCompletedIdx;
120
+ const isDeleteCursor = this.cursorIndex === deleteIdx;
121
+
122
+ add(t.fg("text", " Actions:"));
123
+ const markPrefix = isMarkCursor ? t.fg("accent", ">") : " ";
124
+ const markStyle = isMarkCursor ? "accent" : "text";
125
+ add(`${markPrefix} ${t.fg(markStyle, "[Mark completed]")}`);
126
+ const delPrefix = isDeleteCursor ? t.fg("accent", ">") : " ";
127
+ const delStyle = isDeleteCursor ? "accent" : "text";
128
+ add(`${delPrefix} ${t.fg(delStyle, "[Delete]")}`);
129
+
130
+ add("");
131
+
132
+ // Footer
133
+ add(t.fg("accent", "─".repeat(width)));
134
+ add(t.fg("dim", " ↑↓ navigate · Space toggle · Enter confirm · Esc back"));
135
+
136
+ this.cachedWidth = width;
137
+ this.cachedLines = lines;
138
+ return lines;
139
+ }
140
+
141
+ handleInput(data: string): void {
142
+ if (this._resolved) return;
143
+
144
+ if (matchesKey(data, Key.escape)) {
145
+ this._resolved = true;
146
+ this.done(NO_MANAGE_RESULT);
147
+ return;
148
+ }
149
+
150
+ const maxIdx = this.features.length + 3; // features + toggle + mark_completed + delete
151
+
152
+ if (matchesKey(data, Key.up)) {
153
+ this.cursorIndex = (this.cursorIndex - 1 + maxIdx) % maxIdx;
154
+ this.invalidate();
155
+ this.tui.requestRender();
156
+ return;
157
+ }
158
+
159
+ if (matchesKey(data, Key.down)) {
160
+ this.cursorIndex = (this.cursorIndex + 1) % maxIdx;
161
+ this.invalidate();
162
+ this.tui.requestRender();
163
+ return;
164
+ }
165
+
166
+ const toggleIdx = this.features.length;
167
+ const markCompletedIdx = this.features.length + 1;
168
+ const deleteIdx = this.features.length + 2;
169
+
170
+ // Toggle checkbox on feature row
171
+ if (this.cursorIndex < this.features.length) {
172
+ if (matchesKey(data, Key.enter) || data === INPUT.space) {
173
+ this.toggleSelected(this.cursorIndex);
174
+ this.invalidate();
175
+ this.tui.requestRender();
176
+ return;
177
+ }
178
+ }
179
+
180
+ // Toggle button
181
+ if (this.cursorIndex === toggleIdx) {
182
+ if (matchesKey(data, Key.enter) || data === INPUT.space) {
183
+ const allChecked = this.selectedIndices.size === this.features.length;
184
+ if (allChecked) {
185
+ this.selectedIndices.clear();
186
+ } else {
187
+ for (let i = 0; i < this.features.length; i++) {
188
+ this.selectedIndices.add(i);
189
+ }
190
+ }
191
+ this.invalidate();
192
+ this.tui.requestRender();
193
+ return;
194
+ }
195
+ }
196
+
197
+ // Mark completed action
198
+ if (this.cursorIndex === markCompletedIdx && matchesKey(data, Key.enter)) {
199
+ if (this.selectedIndices.size > 0) {
200
+ this._resolved = true;
201
+ this.done({
202
+ action: "mark_completed" as const,
203
+ slugs: [...this.selectedIndices].map((i) => this.features[i].featureSlug),
204
+ });
205
+ }
206
+ return;
207
+ }
208
+
209
+ // Delete action
210
+ if (this.cursorIndex === deleteIdx && matchesKey(data, Key.enter)) {
211
+ if (this.selectedIndices.size > 0) {
212
+ this._resolved = true;
213
+ this.done({
214
+ action: "delete" as const,
215
+ slugs: [...this.selectedIndices].map((i) => this.features[i].featureSlug),
216
+ });
217
+ }
218
+ return;
219
+ }
220
+ }
221
+
222
+ private toggleSelected(index: number): void {
223
+ if (this.selectedIndices.has(index)) {
224
+ this.selectedIndices.delete(index);
225
+ } else {
226
+ this.selectedIndices.add(index);
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Open the manage features dialog via ctx.ui.custom().
233
+ * Returns the action result, or null if the user pressed Esc.
234
+ */
235
+ export async function openManageDialog(
236
+ features: FeatureState[],
237
+ ctx: ExtensionContext,
238
+ ): Promise<ManageFeaturesResult | null> {
239
+ return withCoordinator(() =>
240
+ ctx.ui.custom<ManageFeaturesResult | null>(
241
+ (tui, theme, _kb, done: (result: ManageFeaturesResult | null) => void) =>
242
+ new ManageFeaturesComponent(features, tui, theme, done),
243
+ {},
244
+ ),
245
+ );
246
+ }
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
3
+
4
+ /**
5
+ * UI helper functions for workflow-monitor.
6
+ */
7
+
8
+ import { getActiveFeatureSlug } from "../shared/workflow-refs.js";
9
+ import { withCoordinator } from "../snippets/vendored/subscribe-to-dialog-coordinator.js";
10
+ import { getLastMessage, withAttention } from "../snippets/vendored/subscribe-to-notifications.js";
11
+
12
+ type SelectOption<T extends string> = { label: string; value: T };
13
+
14
+ export async function selectValue<T extends string>(title: string, options: SelectOption<T>[]): Promise<T> {
15
+ const guard = globalThis.__piCtx;
16
+ const ui = guard?.ui;
17
+ if (!ui?.select) {
18
+ // No UI available — return first option as default
19
+ return (options[0]?.value ?? "cancel") as T;
20
+ }
21
+ const slug = getActiveFeatureSlug();
22
+ const labels = options.map((o) => o.label);
23
+ const detail = [title, slug, getLastMessage()].filter(Boolean).join(" • ");
24
+ const pickedLabel = await withAttention("workflow", detail, () => withCoordinator(() => ui.select(title, labels)));
25
+ const picked = options.find((o) => o.label === pickedLabel);
26
+ return (picked?.value ?? "cancel") as T;
27
+ }