@pellux/goodvibes-agent 1.3.0 → 1.4.2

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 (37) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +0 -1
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
  5. package/dist/package/main.js +55623 -55535
  6. package/docs/README.md +0 -1
  7. package/docs/tools-and-commands.md +0 -3
  8. package/package.json +1 -1
  9. package/release/release-notes.md +7 -156
  10. package/src/agent/competitive-feature-inventory.ts +1 -1
  11. package/src/agent/setup-wizard.ts +28 -21
  12. package/src/cli/tui-startup.ts +2 -2
  13. package/src/input/agent-workspace-activation.ts +2 -2
  14. package/src/input/agent-workspace-categories.ts +3 -20
  15. package/src/input/agent-workspace-onboarding-finish.ts +21 -0
  16. package/src/input/agent-workspace-settings.ts +14 -19
  17. package/src/input/agent-workspace-setup-snapshot.ts +2 -10
  18. package/src/input/agent-workspace-setup.ts +14 -47
  19. package/src/input/agent-workspace-snapshot.ts +0 -7
  20. package/src/input/agent-workspace-types.ts +5 -2
  21. package/src/input/agent-workspace.ts +19 -8
  22. package/src/input/commands.ts +0 -4
  23. package/src/input/handler.ts +70 -5
  24. package/src/main.ts +1 -40
  25. package/src/panels/builtin/agent.ts +0 -28
  26. package/src/renderer/agent-workspace-context-lines.ts +11 -42
  27. package/src/renderer/agent-workspace.ts +414 -31
  28. package/src/renderer/help-overlay.ts +0 -2
  29. package/src/tools/agent-harness-setup-posture.ts +7 -7
  30. package/src/version.ts +1 -1
  31. package/docs/project-planning.md +0 -81
  32. package/src/input/commands/planning-runtime.ts +0 -215
  33. package/src/input/commands/work-plan-runtime.ts +0 -191
  34. package/src/panels/plan-dashboard-panel.ts +0 -274
  35. package/src/panels/project-planning-panel.ts +0 -721
  36. package/src/panels/work-plan-panel.ts +0 -175
  37. package/src/planning/project-planning-coordinator.ts +0 -543
@@ -30,7 +30,7 @@ export async function setupPostureCatalogStatus(context: CommandContext): Promis
30
30
  autonomyBlockers: plan.filter((item) => item.blocksAutonomy && item.status !== 'ready').length,
31
31
  nextSetupHandoffs: nextSetupHandoffSummaries(plan, 5),
32
32
  setupWizard,
33
- setupCloseout: setupWizard.closeout,
33
+ setupCloseout: setupWizard._diagnostic.closeout,
34
34
  collectionIssues: snapshot.collectionIssues.length,
35
35
  setupMarkerExists: setupCompletionMarkerExists(context),
36
36
  setupSmokeEvidence,
@@ -81,7 +81,7 @@ export async function setupRepairSummary(context: CommandContext, args: AgentHar
81
81
  status: setupWizard.status,
82
82
  currentStepId: setupWizard.currentStepId,
83
83
  currentStepLabel: setupWizard.currentStepLabel,
84
- closeout: setupWizard.closeout,
84
+ closeout: setupWizard._diagnostic.closeout,
85
85
  },
86
86
  routes: {
87
87
  inspectSetup: DEFAULT_AGENT_SETUP_WIZARD_REVIEW_ROUTE,
@@ -149,14 +149,14 @@ export async function setupPostureSummary(context: CommandContext, args: AgentHa
149
149
  progressLabel: setupWizard.progressLabel,
150
150
  currentStepId: setupWizard.currentStepId,
151
151
  currentStepLabel: setupWizard.currentStepLabel,
152
- repeatedBlocker: setupWizard.repeatedBlocker,
152
+ repeatedBlocker: setupWizard._diagnostic.repeatedBlocker,
153
153
  },
154
- setupCloseout: setupWizard.closeout,
154
+ setupCloseout: setupWizard._diagnostic.closeout,
155
155
  },
156
156
  setupSmokeEvidence,
157
157
  setupSmokeHistory,
158
158
  setupWizard,
159
- setupCloseout: setupWizard.closeout,
159
+ setupCloseout: setupWizard._diagnostic.closeout,
160
160
  currentRoute: snapshot.providerRouting,
161
161
  issues: snapshot.collectionIssues,
162
162
  readinessPlan: filteredPlan.map((item) => describePlanItem(item, includeParameters)),
@@ -175,7 +175,7 @@ export async function setupCheckpointSummary(context: CommandContext): Promise<R
175
175
  const setupWizard = buildSetupWizard(plan, context);
176
176
  return {
177
177
  mode: 'setup_checkpoint',
178
- checkpoint: setupWizard.checkpoint,
178
+ checkpoint: setupWizard._diagnostic.checkpoint,
179
179
  currentStep: setupWizard.currentStepId
180
180
  ? setupWizard.steps.find((step) => step.id === setupWizard.currentStepId) ?? null
181
181
  : null,
@@ -254,7 +254,7 @@ export async function markSetupCheckpoint(context: CommandContext, args: AgentHa
254
254
  progressLabel: updatedWizard.progressLabel,
255
255
  currentStepId: updatedWizard.currentStepId,
256
256
  currentStepLabel: updatedWizard.currentStepLabel,
257
- checkpoint: updatedWizard.checkpoint,
257
+ checkpoint: updatedWizard._diagnostic.checkpoint,
258
258
  },
259
259
  routes: {
260
260
  inspectCheckpoint: DEFAULT_AGENT_SETUP_WIZARD_INSPECT_CHECKPOINT_ROUTE,
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.3.0';
9
+ let _version = '1.4.2';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
12
12
  readonly version?: unknown;
@@ -1,81 +0,0 @@
1
- # Project Planning
2
-
3
- GoodVibes Agent owns the active planning loop for operator work. Planning is part of the Agent main conversation, not a hidden execution flow and not a default knowledge query.
4
-
5
- ## Boundary
6
-
7
- The Agent owns:
8
-
9
- - natural-language planning intent detection in the main terminal conversation;
10
- - one-question-at-a-time clarification;
11
- - the Agent-owned planning loop;
12
- - transcript-first planning summaries and the Agent operator workspace planning state;
13
- - explicit execution approval;
14
- - delegation metadata when work needs GoodVibes TUI.
15
-
16
- The connected-host runtime owns durable storage and route contracts:
17
-
18
- - planning namespaces such as `project:<projectId>`;
19
- - readiness evaluation and next-question hints;
20
- - decision records;
21
- - task, dependency, verification, and assignment metadata;
22
- - read-only public operator methods.
23
-
24
- Other surfaces can store or inspect planning artifacts, but conversation control stays in Agent. Agent planning state is not default knowledge, another product segment, or arbitrary knowledge data.
25
-
26
- ## Agent Behavior
27
-
28
- The Agent derives a stable `projectId` from the workspace path and passes it to the connected-host planning service. Planning artifacts are stored under the matching planning namespace so unrelated workspaces do not share state.
29
-
30
- Normal conversation can start planning when the user asks for an execution strategy, dependency graph, verification gates, or delegation handoff. The Agent then:
31
-
32
- - prints a concise planning summary in the main transcript;
33
- - persists the current planning state through public connected-host runtime routes;
34
- - records active open questions and user answers;
35
- - calls readiness evaluation for gaps and the suggested next question;
36
- - asks one focused question instead of executing prematurely.
37
-
38
- The planning loop can be paused with natural language such as "stop planning" or "pause planning".
39
-
40
- ## Planning View
41
-
42
- The planning view is transcript-first. It shows:
43
-
44
- - workspace project id and planning namespace;
45
- - readiness and approval state;
46
- - goal, scope, known context, and current next question;
47
- - blocking or advisory readiness gaps;
48
- - task graph and verification gates;
49
- - delegation candidates;
50
- - durable decisions;
51
- - project language and ambiguity resolutions.
52
-
53
- Keyboard behavior stays in the main Agent TUI: predictable prompt editing, scroll, submit, dismiss, and return-to-chat behavior.
54
-
55
- ## `/plan`
56
-
57
- `/plan` remains a command workspace for inspection and seeding:
58
-
59
- - `/plan` prints current planning readiness and the next planning question;
60
- - `/plan panel` is guidance-only in Agent and points back to `/plan status` or `/plan list`;
61
- - `/plan approve` records explicit execution approval;
62
- - `/plan <goal>` seeds Agent workspace planning state;
63
- - `/plan list` and `/plan show <id>` inspect older execution-plan records.
64
-
65
- Use natural language such as "pause planning" or "stop planning" when planning should pause and normal chat should continue.
66
-
67
- ## Work Plan
68
-
69
- The work-plan tracker is for concrete, durable task state after work becomes actionable. It is separate from the planning interview loop.
70
-
71
- Use `/workplan` when the work already has tasks and needs status tracking. Use `/delegate` when explicit build/fix/review work should go to GoodVibes TUI; include the original ask, delegation reason, success evidence, workspace/worktree hint, priority, and explicit review intent.
72
-
73
- The model can keep the same visible work plan current with `agent_work_plan`. Approved items can be dispatched through confirmed `agent_work_plan action:"dispatch_agents"` into visible `agent` spawn or batch-spawn calls, with linked-agent receipts written back to the plan and post-dispatch next routes for orchestration, work-plan detail, agent inspect/wait/message/cancel, and closeout. Those linked receipts and matching remote-runner artifacts appear in `agent_harness mode:"agent_orchestration"` closeout cards with exact work-plan and artifact review routes. It can also inspect planning/work-plan visibility through `agent_harness` workspace and panel metadata, and inspect explicit build-delegation posture with `delegation action:"status|routes|route"`; destructive work-plan changes, agent dispatch, and delegated submissions still require explicit user request and confirmation.
74
-
75
- ## Agent Knowledge Boundary
76
-
77
- Planning may link to Agent Knowledge evidence, but it must not query or ingest through default knowledge. Source-backed facts for Agent belong under:
78
-
79
- ```text
80
- /api/goodvibes-agent/knowledge/*
81
- ```
@@ -1,215 +0,0 @@
1
- import type {
2
- ProjectPlanningEvaluation,
3
- ProjectPlanningQuestion,
4
- ProjectPlanningService,
5
- ProjectPlanningState,
6
- } from '@pellux/goodvibes-sdk/platform/knowledge';
7
- import type { CommandRegistry } from '../command-registry.ts';
8
- import { requirePlanManager, requireSessionLineageTracker } from './runtime-services.ts';
9
- import { requireYesFlag, stripYesFlag } from './confirmation.ts';
10
-
11
- function recordNextQuestion(
12
- state: Partial<ProjectPlanningState>,
13
- question: ProjectPlanningQuestion | undefined,
14
- ): Partial<ProjectPlanningState> {
15
- if (!question) return state;
16
- const answered = new Set((state.answeredQuestions ?? []).map((entry) => entry.id));
17
- if (answered.has(question.id)) return state;
18
- const openQuestions = [...(state.openQuestions ?? [])];
19
- const existingIndex = openQuestions.findIndex((entry) => entry.id === question.id);
20
- const normalized = { ...question, status: question.status ?? 'open' } satisfies ProjectPlanningQuestion;
21
- if (existingIndex >= 0) openQuestions[existingIndex] = normalized;
22
- else openQuestions.unshift(normalized);
23
- return { ...state, openQuestions };
24
- }
25
-
26
- async function persistEvaluatedNextQuestion(
27
- service: ProjectPlanningService,
28
- projectId: string,
29
- state: ProjectPlanningState,
30
- evaluation: ProjectPlanningEvaluation,
31
- ): Promise<{ state: ProjectPlanningState; evaluation: ProjectPlanningEvaluation }> {
32
- if (!evaluation.nextQuestion) return { state, evaluation };
33
- if (state.openQuestions.some((question) => question.id === evaluation.nextQuestion?.id)) {
34
- return { state, evaluation };
35
- }
36
- const withQuestion = recordNextQuestion(evaluation.state ?? state, evaluation.nextQuestion);
37
- const saved = await service.upsertState({ projectId, state: withQuestion });
38
- const nextState = saved.state ?? state;
39
- const nextEvaluation = await service.evaluate({ projectId, state: nextState });
40
- return { state: nextState, evaluation: nextEvaluation };
41
- }
42
-
43
- function formatNextQuestion(question: ProjectPlanningQuestion | undefined): string {
44
- if (!question) return 'No next question recorded.';
45
- const lines = [`Next question: ${question.prompt}`];
46
- if (question.recommendedAnswer) lines.push(`Recommended answer: ${question.recommendedAnswer}`);
47
- lines.push('Answer in the main prompt or review planning state with /plan status.');
48
- return lines.join('\n');
49
- }
50
-
51
- export function registerPlanningRuntimeCommands(registry: CommandRegistry): void {
52
- registry.register({
53
- name: 'plan',
54
- description: 'Inspect or seed Agent workspace planning state',
55
- usage: '[approve --yes | list | show <id> | mode | explain | override <strategy> --yes | status | clear --yes | <planning goal>]',
56
- argsHint: '[approve|status|list|<goal>]',
57
- async handler(args, ctx) {
58
- const planManager = requirePlanManager(ctx);
59
- const sessionLineageTracker = requireSessionLineageTracker(ctx);
60
- const plannerSubs = ['mode', 'explain', 'override', 'status', 'clear'];
61
- if (args.length > 0 && plannerSubs.includes(args[0].toLowerCase())) {
62
- const parsed = stripYesFlag(args);
63
- const subcommand = parsed.rest[0]?.toLowerCase() ?? '';
64
- if ((subcommand === 'override' || subcommand === 'clear') && !parsed.yes) {
65
- requireYesFlag(ctx, `${subcommand} planner state`, `/plan ${subcommand}${subcommand === 'override' ? ' <strategy>' : ''} --yes`);
66
- return;
67
- }
68
- const result = ctx.ops.planRuntime
69
- ? ctx.ops.planRuntime(parsed.rest[0] ?? args[0], parsed.rest.slice(1))
70
- : { ok: false, output: 'Plan runtime route is not available in this runtime.' };
71
- ctx.print(result.output);
72
- return;
73
- }
74
-
75
- const projectPlanningService = ctx.workspace.projectPlanningService;
76
- const projectId = ctx.workspace.projectPlanningProjectId;
77
-
78
- if (args.length === 0) {
79
- if (projectPlanningService && projectId) {
80
- const [status, stateResult] = await Promise.all([
81
- projectPlanningService.status({ projectId }),
82
- projectPlanningService.getState({ projectId }),
83
- ]);
84
- const initialEvaluation = await projectPlanningService.evaluate({
85
- projectId,
86
- ...(stateResult.state ? { state: stateResult.state } : {}),
87
- });
88
- const { evaluation } = stateResult.state
89
- ? await persistEvaluatedNextQuestion(projectPlanningService, projectId, stateResult.state, initialEvaluation)
90
- : { evaluation: initialEvaluation };
91
- const planningNamespace = String(
92
- status[['knowledge', 'SpaceId'].join('') as keyof typeof status] ?? `project:${status.projectId}`,
93
- );
94
- ctx.print(
95
- `Project planning: ${evaluation.readiness}\n` +
96
- `Project: ${status.projectId}\n` +
97
- `Planning namespace: ${planningNamespace}\n` +
98
- `Artifacts: ${status.counts.states} state, ${status.counts.decisions} decisions, ${status.counts.languageArtifacts} language\n` +
99
- formatNextQuestion(evaluation.nextQuestion),
100
- );
101
- return;
102
- }
103
- const active = planManager.getActive(ctx.session.runtime.sessionId);
104
- if (!active) {
105
- ctx.print('No active execution plan.');
106
- return;
107
- }
108
- const summary = planManager.getSummary(active);
109
- ctx.print(`Active plan: "${active.title}" [${active.status.toUpperCase()}]\n${summary}`);
110
- return;
111
- }
112
-
113
- if (args[0] === 'panel') {
114
- ctx.print('Use /plan status or /plan list for compact command output. Open Agent Workspace -> Work -> Planning status or Saved plans for the workspace view.');
115
- return;
116
- }
117
-
118
- if (args[0] === 'approve') {
119
- const parsed = stripYesFlag(args);
120
- if (!parsed.yes) {
121
- requireYesFlag(ctx, 'approve project planning state for execution', '/plan approve --yes');
122
- return;
123
- }
124
- if (!projectPlanningService || !projectId) {
125
- ctx.print('Project planning service is not available in this runtime.');
126
- return;
127
- }
128
- const current = await projectPlanningService.getState({ projectId });
129
- if (!current.state) {
130
- ctx.print('No project planning state exists to approve.');
131
- return;
132
- }
133
- const result = await projectPlanningService.upsertState({
134
- projectId,
135
- state: {
136
- ...current.state,
137
- executionApproved: true,
138
- metadata: {
139
- ...(current.state.metadata ?? {}),
140
- approvedFrom: 'plan-command',
141
- approvedAt: Date.now(),
142
- },
143
- },
144
- });
145
- const evaluation = await projectPlanningService.evaluate({ projectId });
146
- ctx.print(`Project planning approved. Readiness: ${evaluation.readiness}. State: ${result.state?.id ?? 'current'}.`);
147
- return;
148
- }
149
-
150
- if (args[0] === 'list') {
151
- const plans = planManager.list();
152
- if (plans.length === 0) {
153
- ctx.print('No plans found.');
154
- return;
155
- }
156
- ctx.print(`Plans (${plans.length}):\n${plans.map((plan) => {
157
- const marker = plan.status === 'active' ? '▶' : ' ';
158
- return ` ${marker} ${plan.id.slice(0, 8)} [${plan.status.padEnd(8)}] ${plan.title}`;
159
- }).join('\n')}`);
160
- return;
161
- }
162
-
163
- if (args[0] === 'show') {
164
- const id = args[1];
165
- if (!id) {
166
- ctx.print('Usage: /plan show <plan-id>');
167
- return;
168
- }
169
- const plans = planManager.list();
170
- const plan = plans.find((entry) => entry.id === id || entry.id.startsWith(id));
171
- if (!plan) {
172
- ctx.print(`Plan not found: ${id}`);
173
- return;
174
- }
175
- ctx.print(planManager.toMarkdown(plan));
176
- return;
177
- }
178
-
179
- const taskDescription = args.join(' ');
180
- if (!projectPlanningService || !projectId) {
181
- ctx.print('Project planning service is not available in this runtime.');
182
- return;
183
- }
184
- const result = await projectPlanningService.upsertState({
185
- projectId,
186
- state: {
187
- goal: taskDescription,
188
- knownContext: [
189
- `Workspace planning was seeded from the Agent /plan command.`,
190
- ],
191
- metadata: {
192
- active: true,
193
- owner: 'agent',
194
- source: 'plan-command',
195
- lastPromptAt: Date.now(),
196
- },
197
- },
198
- });
199
- const initialEvaluation = await projectPlanningService.evaluate({
200
- projectId,
201
- ...(result.state ? { state: result.state } : {}),
202
- });
203
- const { state, evaluation } = result.state
204
- ? await persistEvaluatedNextQuestion(projectPlanningService, projectId, result.state, initialEvaluation)
205
- : { state: result.state, evaluation: initialEvaluation };
206
- sessionLineageTracker.setOriginalTask(taskDescription.slice(0, 200));
207
-
208
- ctx.print(
209
- `Project planning seeded: "${state?.goal ?? taskDescription}"\n` +
210
- `Readiness: ${evaluation.readiness}\n` +
211
- formatNextQuestion(evaluation.nextQuestion),
212
- );
213
- },
214
- });
215
- }
@@ -1,191 +0,0 @@
1
- import type { CommandRegistry } from '../command-registry.ts';
2
- import type { WorkPlanItemStatus, WorkPlanStore } from '../../work-plans/work-plan-store.ts';
3
- import { WORK_PLAN_STATUSES } from '../../work-plans/work-plan-store.ts';
4
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
5
- import { requireYesFlag, stripYesFlag } from './confirmation.ts';
6
-
7
- const STATUS_COMMANDS: Record<string, WorkPlanItemStatus> = {
8
- pending: 'pending',
9
- todo: 'pending',
10
- start: 'in_progress',
11
- active: 'in_progress',
12
- progress: 'in_progress',
13
- block: 'blocked',
14
- blocked: 'blocked',
15
- done: 'done',
16
- complete: 'done',
17
- fail: 'failed',
18
- failed: 'failed',
19
- cancel: 'cancelled',
20
- cancelled: 'cancelled',
21
- };
22
-
23
- function getStore(ctx: import('../command-registry.ts').CommandContext): WorkPlanStore | null {
24
- return ctx.workspace.workPlanStore ?? null;
25
- }
26
-
27
- function formatList(store: WorkPlanStore): string {
28
- const items = store.listItems();
29
- if (items.length === 0) {
30
- return [
31
- 'Work plan is empty',
32
- ' next /workplan add <title>',
33
- ].join('\n');
34
- }
35
- return [
36
- `Work Plan (${items.length})`,
37
- ...items.map((item) => {
38
- const owner = item.owner ? ` @${item.owner}` : '';
39
- return ` ${item.id} ${item.status.padEnd(11)} ${item.title}${owner}`;
40
- }),
41
- ].join('\n');
42
- }
43
-
44
- function parseAddArgs(args: string[]): { title: string; owner?: string; source?: string; notes?: string } {
45
- const titleParts: string[] = [];
46
- let owner: string | undefined;
47
- let source: string | undefined;
48
- let notes: string | undefined;
49
- for (let i = 0; i < args.length; i++) {
50
- const part = args[i] ?? '';
51
- if (part === '--owner' && args[i + 1]) {
52
- owner = args[++i];
53
- continue;
54
- }
55
- if (part === '--source' && args[i + 1]) {
56
- source = args[++i];
57
- continue;
58
- }
59
- if (part === '--notes' && args[i + 1]) {
60
- notes = args.slice(i + 1).join(' ').trim();
61
- break;
62
- }
63
- titleParts.push(part);
64
- }
65
- return {
66
- title: titleParts.join(' ').trim(),
67
- ...(owner ? { owner } : {}),
68
- ...(source ? { source } : {}),
69
- ...(notes ? { notes } : {}),
70
- };
71
- }
72
-
73
- export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void {
74
- registry.register({
75
- name: 'workplan',
76
- aliases: ['wp', 'todo'],
77
- description: 'Track a persistent workspace-scoped work plan',
78
- usage: '[list|show|add <title> [--owner name] [--source label] [--notes text]|done <id>|start <id>|block <id>|fail <id>|cancel <id>|pending <id>|remove <id> --yes|clear-done --yes]',
79
- argsHint: '[list|add|show|done]',
80
- handler(args, ctx) {
81
- const parsed = stripYesFlag(args);
82
- const commandArgs = [...parsed.rest];
83
- const store = getStore(ctx);
84
- if (!store) {
85
- ctx.print('Work plan store is not available in this runtime.');
86
- return;
87
- }
88
- const subcommand = (commandArgs[0] ?? 'list').toLowerCase();
89
- try {
90
- if (subcommand === 'panel' || subcommand === 'open') {
91
- ctx.print('Open Agent Workspace -> Work -> Work plan for the workspace view, or run /workplan list for compact command output.');
92
- return;
93
- }
94
- if (subcommand === 'list') {
95
- ctx.print(formatList(store));
96
- return;
97
- }
98
- if (subcommand === 'show' || subcommand === 'markdown') {
99
- ctx.print(store.toMarkdown());
100
- return;
101
- }
102
- if (subcommand === 'add') {
103
- const addArgs = parseAddArgs(commandArgs.slice(1));
104
- if (!addArgs.title) {
105
- ctx.print('Usage: /workplan add <title> [--owner name] [--source label] [--notes text]');
106
- return;
107
- }
108
- const addOptions = {
109
- ...(addArgs.owner ? { owner: addArgs.owner } : {}),
110
- source: addArgs.source ?? 'manual',
111
- ...(addArgs.notes ? { notes: addArgs.notes } : {}),
112
- };
113
- const item = store.addItem(addArgs.title, addOptions);
114
- ctx.print([
115
- 'Added work plan item',
116
- ` id ${item.id}`,
117
- ` title ${item.title}`,
118
- ' next open Agent Workspace -> Work -> Work plan to review',
119
- ].join('\n'));
120
- return;
121
- }
122
- if (subcommand === 'remove' || subcommand === 'delete' || subcommand === 'rm') {
123
- const id = commandArgs[1];
124
- if (!id) {
125
- ctx.print(`Usage: /workplan ${subcommand} <id> --yes`);
126
- return;
127
- }
128
- if (!parsed.yes) {
129
- requireYesFlag(ctx, `remove work plan item ${id}`, `/workplan ${subcommand} <id> --yes`);
130
- return;
131
- }
132
- const item = store.removeItem(id);
133
- ctx.print([
134
- 'Removed work plan item',
135
- ` id ${item.id}`,
136
- ` title ${item.title}`,
137
- ].join('\n'));
138
- return;
139
- }
140
- if (subcommand === 'clear-done' || subcommand === 'clear-completed') {
141
- if (!parsed.yes) {
142
- requireYesFlag(ctx, 'clear completed work plan items', `/workplan ${subcommand} --yes`);
143
- return;
144
- }
145
- const count = store.clearCompleted();
146
- ctx.print([
147
- 'Cleared completed/cancelled work plan items',
148
- ` count ${count}`,
149
- ].join('\n'));
150
- return;
151
- }
152
- if (subcommand === 'cycle' || subcommand === 'toggle') {
153
- const id = commandArgs[1];
154
- if (!id) {
155
- ctx.print(`Usage: /workplan ${subcommand} <id>`);
156
- return;
157
- }
158
- const item = store.cycleItemStatus(id);
159
- ctx.print([
160
- 'Updated work plan item',
161
- ` id ${item.id}`,
162
- ` status ${item.status}`,
163
- ].join('\n'));
164
- return;
165
- }
166
- const status = STATUS_COMMANDS[subcommand];
167
- if (status) {
168
- const id = commandArgs[1];
169
- if (!id) {
170
- ctx.print(`Usage: /workplan ${subcommand} <id>`);
171
- return;
172
- }
173
- const item = store.setItemStatus(id, status);
174
- ctx.print([
175
- 'Updated work plan item',
176
- ` id ${item.id}`,
177
- ` status ${item.status}`,
178
- ].join('\n'));
179
- return;
180
- }
181
- if (WORK_PLAN_STATUSES.includes(subcommand as WorkPlanItemStatus)) {
182
- ctx.print(`Usage: /workplan ${subcommand} <id>`);
183
- return;
184
- }
185
- ctx.print(`Unknown workplan subcommand ${subcommand}`);
186
- } catch (error) {
187
- ctx.print(summarizeError(error));
188
- }
189
- },
190
- });
191
- }