clawspec 1.0.6 → 1.0.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawspec",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin that orchestrates OpenSpec project workflows with visible main-agent execution.",
6
6
  "keywords": [
@@ -587,6 +587,7 @@ export class ClawSpecService {
587
587
  scaffoldOnly: planningContext.scaffoldOnly,
588
588
  mode: "sync",
589
589
  prefetchedInstructions: instructionResults.map((result) => result.parsed!).filter(Boolean),
590
+ prefetchedInstructionCommands: instructionResults.map((result) => result.command).filter(Boolean),
590
591
  }),
591
592
  };
592
593
  }
@@ -179,6 +179,7 @@ export function buildPlanningPrependContext(params: {
179
179
  mode: "discussion" | "sync";
180
180
  nextActionHint?: "plan" | "work";
181
181
  prefetchedInstructions?: OpenSpecInstructionsResponse[];
182
+ prefetchedInstructionCommands?: string[];
182
183
  }): string {
183
184
  const project = params.project;
184
185
 
@@ -198,6 +199,7 @@ export function buildPlanningPrependContext(params: {
198
199
  "10. Stop after planning artifacts are refreshed and apply-ready. Do not implement code in this turn.",
199
200
  "11. End with a concise summary and a mandatory final line exactly in this shape: `Next: run `cs-work` to start implementation.`",
200
201
  "12. Never scan sibling directories under `openspec/changes`, never switch to another change, and never restore or rewrite unrelated files.",
202
+ "13. Do not claim that OpenSpec instructions were skipped in this sync turn. The plugin already executed those commands before this turn began.",
201
203
  ]
202
204
  : [
203
205
  "Discussion rules for this turn:",
@@ -243,6 +245,15 @@ export function buildPlanningPrependContext(params: {
243
245
  `- ${instruction.artifactId}: ${displayPath(resolveProjectScopedPath(project, instruction.outputPath))}`,
244
246
  )
245
247
  : []),
248
+ ...(params.mode === "sync"
249
+ ? (params.prefetchedInstructions ?? []).flatMap((instruction) => [
250
+ "",
251
+ formatPrefetchedInstructionBlock(project, instruction),
252
+ ])
253
+ : []),
254
+ params.mode === "sync" ? "" : "",
255
+ params.mode === "sync" ? "OpenSpec commands already executed by the plugin before this turn:" : "",
256
+ ...(params.mode === "sync" ? (params.prefetchedInstructionCommands ?? []).map((command) => `- ${command}`) : []),
246
257
  params.scaffoldOnly ? "" : "",
247
258
  params.scaffoldOnly ? "Only the change scaffold exists right now. That is expected before planning sync generates the first artifacts." : "",
248
259
  "",
@@ -503,3 +514,17 @@ function relativeChangeFile(project: ProjectState, targetPath: string): string {
503
514
  function displayPath(targetPath: string): string {
504
515
  return targetPath.split(path.sep).join("/");
505
516
  }
517
+
518
+ function formatPrefetchedInstructionBlock(
519
+ project: ProjectState,
520
+ instruction: OpenSpecInstructionsResponse,
521
+ ): string {
522
+ const outputPath = displayPath(resolveProjectScopedPath(project, instruction.outputPath));
523
+ return [
524
+ `Artifact ${instruction.artifactId} (output: ${outputPath})`,
525
+ "Instruction:",
526
+ fence(instruction.instruction || "(empty)", "markdown"),
527
+ "Template:",
528
+ fence(instruction.template || "(empty)", "markdown"),
529
+ ].join("\n");
530
+ }
@@ -133,7 +133,7 @@ test("apply reports no new planning notes when the chat context is detached", as
133
133
 
134
134
  test("cs-plan runs visible planning sync and writes a fresh snapshot", async () => {
135
135
  const harness = await createServiceHarness("clawspec-visible-plan-");
136
- const { service, stateStore, repoPath } = harness;
136
+ const { service, stateStore, repoPath, openSpec } = harness;
137
137
  const channelKey = "discord:visible-plan:default:main";
138
138
  const promptContext = {
139
139
  trigger: "user",
@@ -149,6 +149,13 @@ test("cs-plan runs visible planning sync and writes a fresh snapshot", async ()
149
149
  await service.proposalProject(channelKey, "demo-change Demo change");
150
150
  await service.recordPlanningMessageFromContext(promptContext, "add another API endpoint");
151
151
 
152
+ const instructionCalls: string[] = [];
153
+ const originalInstructionsArtifact = openSpec.instructionsArtifact;
154
+ openSpec.instructionsArtifact = async (...args: unknown[]) => {
155
+ instructionCalls.push(String(args[1]));
156
+ return await originalInstructionsArtifact(...args);
157
+ };
158
+
152
159
  const injected = await service.handleBeforePromptBuild(
153
160
  { prompt: "cs-plan", messages: [] },
154
161
  promptContext,
@@ -157,7 +164,10 @@ test("cs-plan runs visible planning sync and writes a fresh snapshot", async ()
157
164
 
158
165
  assert.match(injected?.prependContext ?? "", /ClawSpec planning sync is active for this turn/);
159
166
  assert.match(injected?.prependContext ?? "", /Prefetched OpenSpec instructions for this turn/);
167
+ assert.match(injected?.prependContext ?? "", /OpenSpec commands already executed by the plugin before this turn/);
168
+ assert.match(injected?.prependContext ?? "", /openspec instructions proposal --change demo-change --json/);
160
169
  assert.match(injected?.prependContext ?? "", /planning-instructions[\\/]+proposal\.json/);
170
+ assert.deepEqual(instructionCalls, ["proposal", "specs", "design", "tasks"]);
161
171
  assert.match(injected?.prependContext ?? "", /mandatory final line exactly in this shape/i);
162
172
  assert.equal(runningProject?.status, "planning");
163
173
  assert.equal(runningProject?.phase, "planning_sync");