@h-rig/planning-plugin 0.0.6-alpha.135 → 0.0.6-alpha.137

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.
@@ -0,0 +1,19 @@
1
+ import type { RuntimeCliContext } from "@rig/core";
2
+ export declare const PLANNING_PLAN_CLI_ID = "planning.plan";
3
+ type CommandOutcome = {
4
+ readonly ok: boolean;
5
+ readonly group: string;
6
+ readonly command: string;
7
+ readonly details?: Record<string, unknown>;
8
+ };
9
+ export declare function executePlan(context: RuntimeCliContext, args: readonly string[]): Promise<CommandOutcome>;
10
+ export declare const planningCliCommands: readonly [{
11
+ readonly id: "planning.plan";
12
+ readonly family: "plan";
13
+ readonly command: "rig plan --text <prd>|--prd <file>";
14
+ readonly description: "Generate a plan from a PRD and optionally materialize task-source tasks.";
15
+ readonly usage: "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]";
16
+ readonly projectRequired: true;
17
+ readonly run: typeof executePlan;
18
+ }];
19
+ export {};
@@ -0,0 +1,125 @@
1
+ // @bun
2
+ // packages/planning-plugin/src/cli.ts
3
+ import { readFileSync } from "fs";
4
+ import { createTask, planWorkspace } from "@rig/client";
5
+
6
+ // packages/planning-plugin/src/planning.ts
7
+ async function spec(input, ports) {
8
+ return ports.spec(input);
9
+ }
10
+ async function clarify(planSpec, context, ports) {
11
+ return ports.clarify(planSpec, context);
12
+ }
13
+ async function plan(planSpec, ports) {
14
+ return ports.plan(planSpec);
15
+ }
16
+ async function specClarifyPlan(input, context, ports) {
17
+ return plan(await clarify(await spec(input, ports), context, ports), ports);
18
+ }
19
+
20
+ // packages/planning-plugin/src/cli.ts
21
+ var PLANNING_PLAN_CLI_ID = "planning.plan";
22
+ function printJson(value) {
23
+ console.log(JSON.stringify(value, null, 2));
24
+ }
25
+ function takeFlag(args, flag) {
26
+ const rest = [...args];
27
+ const index = rest.indexOf(flag);
28
+ if (index < 0)
29
+ return { value: false, rest };
30
+ rest.splice(index, 1);
31
+ return { value: true, rest };
32
+ }
33
+ function takeOption(args, flag) {
34
+ const rest = [...args];
35
+ const index = rest.indexOf(flag);
36
+ if (index < 0)
37
+ return { rest };
38
+ const value = rest[index + 1];
39
+ if (!value || value.startsWith("-"))
40
+ throw new Error(`${flag} requires a value.`);
41
+ rest.splice(index, 2);
42
+ return { value, rest };
43
+ }
44
+ function requireNoExtraArgs(args, usage) {
45
+ if (args.length > 0)
46
+ throw new Error(`Unexpected argument: ${args[0]}
47
+ Usage: ${usage}`);
48
+ }
49
+ function defaultPlanningProvider(now) {
50
+ return {
51
+ spec: (input) => {
52
+ const headings = input.body.split(/\r?\n/).map((line) => line.match(/^#{1,3}\s+(.+)$/)?.[1]?.trim()).filter((line) => Boolean(line));
53
+ const titles = headings.length > 0 ? headings : [input.title];
54
+ return {
55
+ prdTitle: input.title,
56
+ userStories: [],
57
+ functionalRequirements: [],
58
+ openQuestions: [],
59
+ tasks: titles.map((title, index) => ({
60
+ localId: `task-${index + 1}`,
61
+ title,
62
+ description: `Implement: ${title}`,
63
+ acceptance: [],
64
+ scope: [],
65
+ validationKeys: [],
66
+ dependsOn: index === 0 ? [] : [`task-${index}`],
67
+ parent: null,
68
+ parallelizable: index === 0
69
+ })),
70
+ generatedAt: now()
71
+ };
72
+ },
73
+ clarify: (planSpec) => planSpec,
74
+ plan: (planSpec) => planSpec
75
+ };
76
+ }
77
+ function readPrdText(args) {
78
+ const title = takeOption(args, "--title");
79
+ const text = takeOption(title.rest, "--text");
80
+ const prd = takeOption(text.rest, "--prd");
81
+ const body = text.value ?? (prd.value ? readFileSync(prd.value, "utf8") : null);
82
+ if (!body?.trim())
83
+ throw new Error("rig plan requires --text <prd> or --prd <file>.");
84
+ return { title: title.value?.trim() || "Rig generated plan", body, rest: prd.rest };
85
+ }
86
+ async function executePlan(context, args) {
87
+ const materialize = takeFlag(args, "--materialize");
88
+ const dryRun = takeFlag(materialize.rest, "--dry-run");
89
+ const json = takeFlag(dryRun.rest, "--json");
90
+ if (materialize.value && dryRun.value)
91
+ throw new Error("Pass only one of --materialize or --dry-run.");
92
+ const prd = readPrdText(json.rest);
93
+ requireNoExtraArgs(prd.rest, "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]");
94
+ const willMaterialize = materialize.value && !dryRun.value;
95
+ const now = () => new Date().toISOString();
96
+ const provider = defaultPlanningProvider(now);
97
+ const result = await planWorkspace(context.projectRoot, prd.body, {
98
+ generatePlan: (input) => specClarifyPlan({ title: input.title ?? prd.title, body: input.prd }, {}, provider),
99
+ createTask
100
+ }, { title: prd.title, materialize: willMaterialize });
101
+ if (context.outputMode === "text") {
102
+ if (json.value)
103
+ printJson(result);
104
+ else
105
+ console.log(result.spec.tasks.map((task) => `${task.localId} ${task.title}`).join(`
106
+ `) || "No plan tasks.");
107
+ }
108
+ return { ok: true, group: "plan", command: willMaterialize ? "materialize" : "show", details: result };
109
+ }
110
+ var planningCliCommands = [
111
+ {
112
+ id: PLANNING_PLAN_CLI_ID,
113
+ family: "plan",
114
+ command: "rig plan --text <prd>|--prd <file>",
115
+ description: "Generate a plan from a PRD and optionally materialize task-source tasks.",
116
+ usage: "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]",
117
+ projectRequired: true,
118
+ run: executePlan
119
+ }
120
+ ];
121
+ export {
122
+ planningCliCommands,
123
+ executePlan,
124
+ PLANNING_PLAN_CLI_ID
125
+ };
@@ -1,2 +1,3 @@
1
1
  export * from "./planning";
2
+ export * from "./cli";
2
3
  export * from "./plugin";
package/dist/src/index.js CHANGED
@@ -115,30 +115,147 @@ async function materialize(planSpec, source, options = {}) {
115
115
  }
116
116
  return { planId, created, existing, localIdToTaskId: Object.fromEntries(localIdToTaskId) };
117
117
  }
118
+ // packages/planning-plugin/src/cli.ts
119
+ import { readFileSync } from "fs";
120
+ import { createTask, planWorkspace } from "@rig/client";
121
+ var PLANNING_PLAN_CLI_ID = "planning.plan";
122
+ function printJson(value) {
123
+ console.log(JSON.stringify(value, null, 2));
124
+ }
125
+ function takeFlag(args, flag) {
126
+ const rest = [...args];
127
+ const index = rest.indexOf(flag);
128
+ if (index < 0)
129
+ return { value: false, rest };
130
+ rest.splice(index, 1);
131
+ return { value: true, rest };
132
+ }
133
+ function takeOption(args, flag) {
134
+ const rest = [...args];
135
+ const index = rest.indexOf(flag);
136
+ if (index < 0)
137
+ return { rest };
138
+ const value = rest[index + 1];
139
+ if (!value || value.startsWith("-"))
140
+ throw new Error(`${flag} requires a value.`);
141
+ rest.splice(index, 2);
142
+ return { value, rest };
143
+ }
144
+ function requireNoExtraArgs(args, usage) {
145
+ if (args.length > 0)
146
+ throw new Error(`Unexpected argument: ${args[0]}
147
+ Usage: ${usage}`);
148
+ }
149
+ function defaultPlanningProvider(now) {
150
+ return {
151
+ spec: (input) => {
152
+ const headings = input.body.split(/\r?\n/).map((line) => line.match(/^#{1,3}\s+(.+)$/)?.[1]?.trim()).filter((line) => Boolean(line));
153
+ const titles = headings.length > 0 ? headings : [input.title];
154
+ return {
155
+ prdTitle: input.title,
156
+ userStories: [],
157
+ functionalRequirements: [],
158
+ openQuestions: [],
159
+ tasks: titles.map((title, index) => ({
160
+ localId: `task-${index + 1}`,
161
+ title,
162
+ description: `Implement: ${title}`,
163
+ acceptance: [],
164
+ scope: [],
165
+ validationKeys: [],
166
+ dependsOn: index === 0 ? [] : [`task-${index}`],
167
+ parent: null,
168
+ parallelizable: index === 0
169
+ })),
170
+ generatedAt: now()
171
+ };
172
+ },
173
+ clarify: (planSpec) => planSpec,
174
+ plan: (planSpec) => planSpec
175
+ };
176
+ }
177
+ function readPrdText(args) {
178
+ const title = takeOption(args, "--title");
179
+ const text = takeOption(title.rest, "--text");
180
+ const prd = takeOption(text.rest, "--prd");
181
+ const body = text.value ?? (prd.value ? readFileSync(prd.value, "utf8") : null);
182
+ if (!body?.trim())
183
+ throw new Error("rig plan requires --text <prd> or --prd <file>.");
184
+ return { title: title.value?.trim() || "Rig generated plan", body, rest: prd.rest };
185
+ }
186
+ async function executePlan(context, args) {
187
+ const materialize2 = takeFlag(args, "--materialize");
188
+ const dryRun = takeFlag(materialize2.rest, "--dry-run");
189
+ const json = takeFlag(dryRun.rest, "--json");
190
+ if (materialize2.value && dryRun.value)
191
+ throw new Error("Pass only one of --materialize or --dry-run.");
192
+ const prd = readPrdText(json.rest);
193
+ requireNoExtraArgs(prd.rest, "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]");
194
+ const willMaterialize = materialize2.value && !dryRun.value;
195
+ const now = () => new Date().toISOString();
196
+ const provider = defaultPlanningProvider(now);
197
+ const result = await planWorkspace(context.projectRoot, prd.body, {
198
+ generatePlan: (input) => specClarifyPlan({ title: input.title ?? prd.title, body: input.prd }, {}, provider),
199
+ createTask
200
+ }, { title: prd.title, materialize: willMaterialize });
201
+ if (context.outputMode === "text") {
202
+ if (json.value)
203
+ printJson(result);
204
+ else
205
+ console.log(result.spec.tasks.map((task) => `${task.localId} ${task.title}`).join(`
206
+ `) || "No plan tasks.");
207
+ }
208
+ return { ok: true, group: "plan", command: willMaterialize ? "materialize" : "show", details: result };
209
+ }
210
+ var planningCliCommands = [
211
+ {
212
+ id: PLANNING_PLAN_CLI_ID,
213
+ family: "plan",
214
+ command: "rig plan --text <prd>|--prd <file>",
215
+ description: "Generate a plan from a PRD and optionally materialize task-source tasks.",
216
+ usage: "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]",
217
+ projectRequired: true,
218
+ run: executePlan
219
+ }
220
+ ];
118
221
  // packages/planning-plugin/src/plugin.ts
119
222
  import { definePlugin } from "@rig/core";
120
223
  var PLANNING_PLUGIN_NAME = "@rig/planning-plugin";
224
+ var PLANNING_PLAN_PANEL_ID = "plan-intake";
121
225
  var planningPlugin = definePlugin({
122
226
  name: PLANNING_PLUGIN_NAME,
123
227
  version: "0.0.0-alpha.1",
124
228
  provides: [],
125
229
  requires: [],
126
230
  contributes: {
127
- cliCommands: [
128
- {
129
- id: "planning.materialize",
130
- command: "rig plan materialize",
131
- description: "Generate a plan from a PRD and materialize idempotent task-source tasks."
132
- }
133
- ]
231
+ capabilities: [
232
+ { id: "planning.plan", title: "PRD-to-task planning", commandId: PLANNING_PLAN_CLI_ID, panelId: PLANNING_PLAN_PANEL_ID }
233
+ ],
234
+ panels: [
235
+ { id: PLANNING_PLAN_PANEL_ID, slot: "capability", title: "Plan intake", capabilityId: "planning.plan" }
236
+ ],
237
+ cliCommands: planningCliCommands.map(({ run: _run, ...metadata }) => metadata)
134
238
  }
239
+ }, {
240
+ featureCapabilities: [
241
+ { id: "planning.plan", title: "PRD-to-task planning", commandId: PLANNING_PLAN_CLI_ID, panelId: PLANNING_PLAN_PANEL_ID }
242
+ ],
243
+ cliCommands: planningCliCommands
135
244
  });
245
+ function createPlanningPlugin() {
246
+ return planningPlugin;
247
+ }
136
248
  export {
137
249
  specClarifyPlan,
138
250
  spec,
139
251
  planningPlugin,
252
+ planningCliCommands,
140
253
  plan,
141
254
  materialize,
255
+ executePlan,
256
+ createPlanningPlugin,
142
257
  clarify,
143
- PLANNING_PLUGIN_NAME
258
+ PLANNING_PLUGIN_NAME,
259
+ PLANNING_PLAN_PANEL_ID,
260
+ PLANNING_PLAN_CLI_ID
144
261
  };
@@ -1,3 +1,5 @@
1
1
  export declare const PLANNING_PLUGIN_NAME = "@rig/planning-plugin";
2
+ export declare const PLANNING_PLAN_PANEL_ID = "plan-intake";
2
3
  export declare const planningPlugin: import("@rig/core").RigPluginWithRuntime;
4
+ export declare function createPlanningPlugin(): import("@rig/core").RigPluginWithRuntime;
3
5
  export default planningPlugin;
@@ -1,25 +1,158 @@
1
1
  // @bun
2
2
  // packages/planning-plugin/src/plugin.ts
3
3
  import { definePlugin } from "@rig/core";
4
+
5
+ // packages/planning-plugin/src/cli.ts
6
+ import { readFileSync } from "fs";
7
+ import { createTask, planWorkspace } from "@rig/client";
8
+
9
+ // packages/planning-plugin/src/planning.ts
10
+ async function spec(input, ports) {
11
+ return ports.spec(input);
12
+ }
13
+ async function clarify(planSpec, context, ports) {
14
+ return ports.clarify(planSpec, context);
15
+ }
16
+ async function plan(planSpec, ports) {
17
+ return ports.plan(planSpec);
18
+ }
19
+ async function specClarifyPlan(input, context, ports) {
20
+ return plan(await clarify(await spec(input, ports), context, ports), ports);
21
+ }
22
+
23
+ // packages/planning-plugin/src/cli.ts
24
+ var PLANNING_PLAN_CLI_ID = "planning.plan";
25
+ function printJson(value) {
26
+ console.log(JSON.stringify(value, null, 2));
27
+ }
28
+ function takeFlag(args, flag) {
29
+ const rest = [...args];
30
+ const index = rest.indexOf(flag);
31
+ if (index < 0)
32
+ return { value: false, rest };
33
+ rest.splice(index, 1);
34
+ return { value: true, rest };
35
+ }
36
+ function takeOption(args, flag) {
37
+ const rest = [...args];
38
+ const index = rest.indexOf(flag);
39
+ if (index < 0)
40
+ return { rest };
41
+ const value = rest[index + 1];
42
+ if (!value || value.startsWith("-"))
43
+ throw new Error(`${flag} requires a value.`);
44
+ rest.splice(index, 2);
45
+ return { value, rest };
46
+ }
47
+ function requireNoExtraArgs(args, usage) {
48
+ if (args.length > 0)
49
+ throw new Error(`Unexpected argument: ${args[0]}
50
+ Usage: ${usage}`);
51
+ }
52
+ function defaultPlanningProvider(now) {
53
+ return {
54
+ spec: (input) => {
55
+ const headings = input.body.split(/\r?\n/).map((line) => line.match(/^#{1,3}\s+(.+)$/)?.[1]?.trim()).filter((line) => Boolean(line));
56
+ const titles = headings.length > 0 ? headings : [input.title];
57
+ return {
58
+ prdTitle: input.title,
59
+ userStories: [],
60
+ functionalRequirements: [],
61
+ openQuestions: [],
62
+ tasks: titles.map((title, index) => ({
63
+ localId: `task-${index + 1}`,
64
+ title,
65
+ description: `Implement: ${title}`,
66
+ acceptance: [],
67
+ scope: [],
68
+ validationKeys: [],
69
+ dependsOn: index === 0 ? [] : [`task-${index}`],
70
+ parent: null,
71
+ parallelizable: index === 0
72
+ })),
73
+ generatedAt: now()
74
+ };
75
+ },
76
+ clarify: (planSpec) => planSpec,
77
+ plan: (planSpec) => planSpec
78
+ };
79
+ }
80
+ function readPrdText(args) {
81
+ const title = takeOption(args, "--title");
82
+ const text = takeOption(title.rest, "--text");
83
+ const prd = takeOption(text.rest, "--prd");
84
+ const body = text.value ?? (prd.value ? readFileSync(prd.value, "utf8") : null);
85
+ if (!body?.trim())
86
+ throw new Error("rig plan requires --text <prd> or --prd <file>.");
87
+ return { title: title.value?.trim() || "Rig generated plan", body, rest: prd.rest };
88
+ }
89
+ async function executePlan(context, args) {
90
+ const materialize = takeFlag(args, "--materialize");
91
+ const dryRun = takeFlag(materialize.rest, "--dry-run");
92
+ const json = takeFlag(dryRun.rest, "--json");
93
+ if (materialize.value && dryRun.value)
94
+ throw new Error("Pass only one of --materialize or --dry-run.");
95
+ const prd = readPrdText(json.rest);
96
+ requireNoExtraArgs(prd.rest, "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]");
97
+ const willMaterialize = materialize.value && !dryRun.value;
98
+ const now = () => new Date().toISOString();
99
+ const provider = defaultPlanningProvider(now);
100
+ const result = await planWorkspace(context.projectRoot, prd.body, {
101
+ generatePlan: (input) => specClarifyPlan({ title: input.title ?? prd.title, body: input.prd }, {}, provider),
102
+ createTask
103
+ }, { title: prd.title, materialize: willMaterialize });
104
+ if (context.outputMode === "text") {
105
+ if (json.value)
106
+ printJson(result);
107
+ else
108
+ console.log(result.spec.tasks.map((task) => `${task.localId} ${task.title}`).join(`
109
+ `) || "No plan tasks.");
110
+ }
111
+ return { ok: true, group: "plan", command: willMaterialize ? "materialize" : "show", details: result };
112
+ }
113
+ var planningCliCommands = [
114
+ {
115
+ id: PLANNING_PLAN_CLI_ID,
116
+ family: "plan",
117
+ command: "rig plan --text <prd>|--prd <file>",
118
+ description: "Generate a plan from a PRD and optionally materialize task-source tasks.",
119
+ usage: "rig plan --text <prd>|--prd <file> [--title <title>] [--materialize|--dry-run] [--json]",
120
+ projectRequired: true,
121
+ run: executePlan
122
+ }
123
+ ];
124
+
125
+ // packages/planning-plugin/src/plugin.ts
4
126
  var PLANNING_PLUGIN_NAME = "@rig/planning-plugin";
127
+ var PLANNING_PLAN_PANEL_ID = "plan-intake";
5
128
  var planningPlugin = definePlugin({
6
129
  name: PLANNING_PLUGIN_NAME,
7
130
  version: "0.0.0-alpha.1",
8
131
  provides: [],
9
132
  requires: [],
10
133
  contributes: {
11
- cliCommands: [
12
- {
13
- id: "planning.materialize",
14
- command: "rig plan materialize",
15
- description: "Generate a plan from a PRD and materialize idempotent task-source tasks."
16
- }
17
- ]
134
+ capabilities: [
135
+ { id: "planning.plan", title: "PRD-to-task planning", commandId: PLANNING_PLAN_CLI_ID, panelId: PLANNING_PLAN_PANEL_ID }
136
+ ],
137
+ panels: [
138
+ { id: PLANNING_PLAN_PANEL_ID, slot: "capability", title: "Plan intake", capabilityId: "planning.plan" }
139
+ ],
140
+ cliCommands: planningCliCommands.map(({ run: _run, ...metadata }) => metadata)
18
141
  }
142
+ }, {
143
+ featureCapabilities: [
144
+ { id: "planning.plan", title: "PRD-to-task planning", commandId: PLANNING_PLAN_CLI_ID, panelId: PLANNING_PLAN_PANEL_ID }
145
+ ],
146
+ cliCommands: planningCliCommands
19
147
  });
148
+ function createPlanningPlugin() {
149
+ return planningPlugin;
150
+ }
20
151
  var plugin_default = planningPlugin;
21
152
  export {
22
153
  planningPlugin,
23
154
  plugin_default as default,
24
- PLANNING_PLUGIN_NAME
155
+ createPlanningPlugin,
156
+ PLANNING_PLUGIN_NAME,
157
+ PLANNING_PLAN_PANEL_ID
25
158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/planning-plugin",
3
- "version": "0.0.6-alpha.135",
3
+ "version": "0.0.6-alpha.137",
4
4
  "type": "module",
5
5
  "description": "First-party PRD-to-plan plugin for Rig task sources.",
6
6
  "license": "UNLICENSED",
@@ -29,7 +29,8 @@
29
29
  "module": "./dist/src/index.js",
30
30
  "types": "./dist/src/index.d.ts",
31
31
  "dependencies": {
32
- "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.135",
33
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.135"
32
+ "@rig/client": "npm:@h-rig/client@0.0.6-alpha.137",
33
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137",
34
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.137"
34
35
  }
35
36
  }