cclaw-cli 0.21.0 → 0.21.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.
@@ -157,7 +157,7 @@ ${rows}
157
157
  function mergedAntiPatterns(schema) {
158
158
  const merged = [];
159
159
  const seen = new Set();
160
- for (const item of [...schema.antiPatterns, ...schema.blockers, ...schema.redFlags]) {
160
+ for (const item of [...schema.commonRationalizations, ...schema.blockers]) {
161
161
  const key = item.trim().toLowerCase();
162
162
  if (seen.has(key))
163
163
  continue;
@@ -94,18 +94,14 @@ export const BRAINSTORM = {
94
94
  "no implementation action taken",
95
95
  "artifact reviewed by user"
96
96
  ],
97
- antiPatterns: [
97
+ commonRationalizations: [
98
98
  "Asking questions without exploring existing project context first",
99
99
  "Asking bundled or purely informational questions that don't change decisions",
100
100
  "Proposing cosmetic option variants instead of architecturally distinct approaches",
101
101
  "Jumping directly into implementation",
102
- "Requesting approval without stating what decision is being approved"
103
- ],
104
- redFlags: [
105
- "No project context exploration before questions",
102
+ "Requesting approval without stating what decision is being approved",
106
103
  "Questions that only gather preferences without design impact",
107
- "Options that are variants of one approach, not distinct alternatives",
108
- "Approval requested without explicit decision context"
104
+ "Options that are variants of one approach, not distinct alternatives"
109
105
  ],
110
106
  policyNeedles: [
111
107
  "Explore project context",
@@ -106,20 +106,17 @@ export const DESIGN = {
106
106
  "completion dashboard present with all review-section statuses",
107
107
  "artifact complete for spec handoff"
108
108
  ],
109
- antiPatterns: [
109
+ commonRationalizations: [
110
110
  "Architecture deferred to implementation phase",
111
111
  "Missing data-flow edge cases",
112
112
  "No performance budget for critical path",
113
113
  "Batching multiple design issues into one question",
114
114
  "Skipping review sections because plan seems simple",
115
115
  "Agreeing with user's architecture choice without evaluating alternatives",
116
- "Hedging every recommendation with 'it depends' instead of taking a position"
117
- ],
118
- redFlags: [
116
+ "Hedging every recommendation with 'it depends' instead of taking a position",
119
117
  "No explicit architecture boundary section",
120
118
  "No failure recovery strategy",
121
119
  "No defined test/perf baseline",
122
- "Review sections skipped or condensed",
123
120
  "No NOT-in-scope output section",
124
121
  "No What-already-exists output section",
125
122
  "Design decisions made without reading the actual code first"
@@ -83,14 +83,12 @@ export const PLAN = {
83
83
  "artifact ready for TDD execution",
84
84
  "acceptance mapping complete"
85
85
  ],
86
- antiPatterns: [
86
+ commonRationalizations: [
87
87
  "Horizontal decomposition without end-to-end slices",
88
88
  "Tasks without verification steps",
89
89
  "Starting execution before approval",
90
90
  "Tasks that touch multiple unrelated areas",
91
- "Using placeholder tokens or scope-reduction phrases (`v1`, `for now`, `later`) in task definitions"
92
- ],
93
- redFlags: [
91
+ "Using placeholder tokens or scope-reduction phrases (`v1`, `for now`, `later`) in task definitions",
94
92
  "No dependency graph",
95
93
  "No WAIT_FOR_CONFIRM marker",
96
94
  "No explicit dependency waves",
@@ -88,14 +88,12 @@ export const REVIEW = {
88
88
  "critical blockers resolved",
89
89
  "ship readiness explicitly stated"
90
90
  ],
91
- antiPatterns: [
91
+ commonRationalizations: [
92
92
  "Single generic review without layered structure",
93
93
  "No severity classification",
94
94
  "Shipping with open criticals",
95
95
  "Batching multiple findings into one report without individual resolution",
96
- "Skipping Layer 2 sections because Layer 1 passed"
97
- ],
98
- redFlags: [
96
+ "Skipping Layer 2 sections because Layer 1 passed",
99
97
  "No separate Layer 1/Layer 2 outcomes",
100
98
  "No structured review-army reconciliation artifact",
101
99
  "No critical bucket",
@@ -74,8 +74,14 @@ export interface StageSchema {
74
74
  outputs: string[];
75
75
  blockers: string[];
76
76
  exitCriteria: string[];
77
- antiPatterns: string[];
78
- redFlags: string[];
77
+ /**
78
+ * Consolidated "Common Rationalizations" list — things an agent is likely to
79
+ * talk itself into that should stop the stage. Rendered under the
80
+ * "Anti-Patterns & Red Flags" heading in the generated SKILL.md. Replaces
81
+ * the former split between `antiPatterns` and `redFlags`, which produced
82
+ * near-duplicate entries and forced downstream code to merge them anyway.
83
+ */
84
+ commonRationalizations: string[];
79
85
  policyNeedles: string[];
80
86
  artifactFile: string;
81
87
  next: FlowStage | "done";
@@ -96,7 +96,7 @@ export const SCOPE = {
96
96
  "completion dashboard produced",
97
97
  "scope summary produced"
98
98
  ],
99
- antiPatterns: [
99
+ commonRationalizations: [
100
100
  "Scope silently expanded during discussion",
101
101
  "No explicit out-of-scope section",
102
102
  "Premise accepted without challenge",
@@ -104,16 +104,12 @@ export const SCOPE = {
104
104
  "Hedged recommendations that avoid taking a position",
105
105
  "Batching multiple scope issues into one question",
106
106
  "Re-arguing for smaller scope after user rejects reduction",
107
- "Using scope-reduction placeholders (`v1`, `for now`, `we can do later`) instead of explicit user-approved boundaries"
108
- ],
109
- redFlags: [
107
+ "Using scope-reduction placeholders (`v1`, `for now`, `we can do later`) instead of explicit user-approved boundaries",
110
108
  "No selected mode in artifact",
111
109
  "Mode selected without heuristic justification",
112
110
  "No discretion section (or explicit `None`) in artifact",
113
111
  "No deferred/not-in-scope section",
114
112
  "No user approval marker",
115
- "Premise challenge missing or superficial",
116
- "No implementation alternatives evaluated",
117
113
  "Missing Locked Decisions section or decisions without D-XX IDs"
118
114
  ],
119
115
  policyNeedles: ["Scope mode", "In Scope", "Out of Scope", "Discretion Areas", "NOT in scope", "Premise Challenge", "Locked Decisions"],
@@ -77,14 +77,12 @@ export const SHIP = {
77
77
  "rollback and release notes complete",
78
78
  "finalization action explicitly chosen and executed"
79
79
  ],
80
- antiPatterns: [
80
+ commonRationalizations: [
81
81
  "Shipping without rollback strategy",
82
82
  "Implicit finalization decision",
83
83
  "Bypassing preflight due to urgency",
84
84
  "Selecting multiple finalization modes",
85
- "Shipping with BLOCKED review verdict"
86
- ],
87
- redFlags: [
85
+ "Shipping with BLOCKED review verdict",
88
86
  "No rollback trigger/steps",
89
87
  "More than one finalization mode implied",
90
88
  "No explicit preflight result",
@@ -77,14 +77,11 @@ export const SPEC = {
77
77
  "plan-ready acceptance mapping exists",
78
78
  "testability confirmed for all criteria"
79
79
  ],
80
- antiPatterns: [
80
+ commonRationalizations: [
81
81
  "High-level goals without measurable outcomes",
82
82
  "Implicit assumptions",
83
83
  "Proceeding to plan before approval",
84
- "Using vague adjectives (fast, intuitive, robust) without thresholds"
85
- ],
86
- redFlags: [
87
- "Criteria use vague language (fast, intuitive, robust) without thresholds",
84
+ "Using vague adjectives (fast, intuitive, robust) without thresholds",
88
85
  "No explicit assumptions section",
89
86
  "No approval record",
90
87
  "No testability mapping",
@@ -88,16 +88,14 @@ export const TDD = {
88
88
  "required gates marked satisfied",
89
89
  "traceability annotated"
90
90
  ],
91
- antiPatterns: [
91
+ commonRationalizations: [
92
92
  "Writing code before failing test",
93
93
  "Asserting implementation details instead of behavior",
94
94
  "Big-bang implementation across multiple slices",
95
95
  "Partial test runs presented as GREEN",
96
96
  "Skipping evidence capture",
97
97
  "Undocumented refactor changes",
98
- "Adding features beyond what RED tests require"
99
- ],
100
- redFlags: [
98
+ "Adding features beyond what RED tests require",
101
99
  "No failing test output (RED missing)",
102
100
  "Implementation edits appear before RED evidence",
103
101
  "No full-suite GREEN evidence",
package/dist/runs.d.ts CHANGED
@@ -52,19 +52,6 @@ export interface ArchiveManifest {
52
52
  snapshottedStateFiles: string[];
53
53
  retro: ArchiveRunResult["retro"];
54
54
  }
55
- export interface RewindRunOptions {
56
- to: FlowStage;
57
- reason?: string;
58
- }
59
- export interface RewindRunResult {
60
- rewindId: string;
61
- from: FlowStage;
62
- to: FlowStage;
63
- invalidatedStages: FlowStage[];
64
- staleArtifacts: string[];
65
- archivePath: string;
66
- nextState: FlowState;
67
- }
68
55
  interface EnsureRunSystemOptions {
69
56
  createIfMissing?: boolean;
70
57
  }
@@ -82,11 +69,6 @@ export declare function writeFlowState(projectRoot: string, state: FlowState, op
82
69
  export declare function ensureRunSystem(projectRoot: string, _options?: EnsureRunSystemOptions): Promise<FlowState>;
83
70
  export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
84
71
  export declare function archiveRun(projectRoot: string, featureName?: string, options?: ArchiveRunOptions): Promise<ArchiveRunResult>;
85
- export declare function rewindRun(projectRoot: string, options: RewindRunOptions): Promise<RewindRunResult>;
86
- export declare function acknowledgeStaleStage(projectRoot: string, stage: FlowStage): Promise<{
87
- acknowledged: boolean;
88
- remaining: FlowStage[];
89
- }>;
90
72
  /**
91
73
  * Counts entries in the canonical JSONL knowledge store. An "active" entry is one
92
74
  * non-empty line that parses as JSON with the required `type` field belonging to the
package/dist/runs.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { COMMAND_FILE_ORDER, RUNTIME_ROOT } from "./constants.js";
4
- import { canTransition, createInitialFlowState, isFlowTrack, skippedStagesForTrack, trackStages } from "./flow-state.js";
4
+ import { canTransition, createInitialFlowState, isFlowTrack, skippedStagesForTrack } from "./flow-state.js";
5
5
  import { ensureFeatureSystem, readActiveFeature, syncActiveFeatureSnapshot } from "./feature-system.js";
6
6
  import { ensureDir, exists, withDirectoryLock, writeFileSafe } from "./fs-utils.js";
7
- import { stageSchema } from "./content/stage-schema.js";
8
7
  export class InvalidStageTransitionError extends Error {
9
8
  from;
10
9
  to;
@@ -36,8 +35,6 @@ const FLOW_STATE_REL_PATH = `${RUNTIME_ROOT}/state/flow-state.json`;
36
35
  const RUNS_DIR_REL_PATH = `${RUNTIME_ROOT}/runs`;
37
36
  const ACTIVE_ARTIFACTS_REL_PATH = `${RUNTIME_ROOT}/artifacts`;
38
37
  const STATE_DIR_REL_PATH = `${RUNTIME_ROOT}/state`;
39
- const REWIND_LOG_REL_PATH = `${RUNTIME_ROOT}/state/rewind-log.jsonl`;
40
- const REWIND_ARCHIVE_DIR_NAME = "_rewind-archive";
41
38
  const FLOW_STAGE_SET = new Set(COMMAND_FILE_ORDER);
42
39
  /** State filenames explicitly excluded from the archive snapshot. */
43
40
  const STATE_SNAPSHOT_EXCLUDE = new Set([
@@ -59,12 +56,6 @@ function activeArtifactsPath(projectRoot) {
59
56
  function stateDirPath(projectRoot) {
60
57
  return path.join(projectRoot, STATE_DIR_REL_PATH);
61
58
  }
62
- function rewindLogPath(projectRoot) {
63
- return path.join(projectRoot, REWIND_LOG_REL_PATH);
64
- }
65
- function rewindArchivePath(projectRoot, rewindId) {
66
- return path.join(activeArtifactsPath(projectRoot), REWIND_ARCHIVE_DIR_NAME, rewindId);
67
- }
68
59
  async function snapshotStateDirectory(projectRoot, destinationRoot) {
69
60
  const sourceDir = stateDirPath(projectRoot);
70
61
  if (!(await exists(sourceDir))) {
@@ -348,23 +339,6 @@ async function uniqueArchiveId(projectRoot, baseId) {
348
339
  }
349
340
  return candidate;
350
341
  }
351
- function rewindTimestampId(date = new Date()) {
352
- return date
353
- .toISOString()
354
- .replace(/[-:]/gu, "")
355
- .replace(/\.\d{3}Z$/u, "Z");
356
- }
357
- function staleArtifactFileName(fileName) {
358
- const ext = path.extname(fileName);
359
- if (!ext) {
360
- return `${fileName}.stale`;
361
- }
362
- const base = fileName.slice(0, -ext.length);
363
- return `${base}.stale${ext}`;
364
- }
365
- function stageIndexMapForTrack(track) {
366
- return new Map(trackStages(track).map((stage, index) => [stage, index]));
367
- }
368
342
  function retroArtifactPath(projectRoot) {
369
343
  return path.join(activeArtifactsPath(projectRoot), "09-retro.md");
370
344
  }
@@ -620,167 +594,6 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
620
594
  retro: retroSummary
621
595
  };
622
596
  }
623
- export async function rewindRun(projectRoot, options) {
624
- await ensureRunSystem(projectRoot);
625
- const state = await readFlowState(projectRoot);
626
- const track = state.track ?? "standard";
627
- const ordered = trackStages(track);
628
- const stageToIndex = stageIndexMapForTrack(track);
629
- const toIndex = stageToIndex.get(options.to);
630
- const currentIndex = stageToIndex.get(state.currentStage);
631
- if (toIndex === undefined) {
632
- throw new Error(`Cannot rewind to "${options.to}" because it is outside track "${track}".`);
633
- }
634
- if (currentIndex === undefined) {
635
- throw new Error(`Current stage "${state.currentStage}" is not part of track "${track}".`);
636
- }
637
- if (toIndex > currentIndex) {
638
- throw new Error(`Cannot rewind forward from "${state.currentStage}" to "${options.to}".`);
639
- }
640
- const reason = options.reason?.trim() && options.reason.trim().length > 0
641
- ? options.reason.trim()
642
- : "manual_rewind";
643
- const nowIso = new Date().toISOString();
644
- const rewindId = `rewind-${rewindTimestampId()}`;
645
- const invalidatedStages = ordered.filter((stage) => {
646
- const idx = stageToIndex.get(stage);
647
- if (idx === undefined || idx <= toIndex) {
648
- return false;
649
- }
650
- return state.completedStages.includes(stage) || stage === state.currentStage;
651
- });
652
- const nextCompletedStages = state.completedStages.filter((stage) => {
653
- const idx = stageToIndex.get(stage);
654
- return typeof idx === "number" && idx < toIndex;
655
- });
656
- const freshCatalog = createInitialFlowState({ activeRunId: state.activeRunId, track }).stageGateCatalog;
657
- const nextCatalog = { ...state.stageGateCatalog };
658
- for (const stage of ordered) {
659
- const idx = stageToIndex.get(stage);
660
- if (idx === undefined)
661
- continue;
662
- if (idx >= toIndex) {
663
- nextCatalog[stage] = {
664
- ...freshCatalog[stage],
665
- required: [...freshCatalog[stage].required],
666
- recommended: [...freshCatalog[stage].recommended],
667
- conditional: [...freshCatalog[stage].conditional],
668
- triggered: [],
669
- passed: [],
670
- blocked: []
671
- };
672
- }
673
- }
674
- const nextGuardEvidence = { ...state.guardEvidence };
675
- for (const stage of ordered) {
676
- const idx = stageToIndex.get(stage);
677
- if (idx === undefined || idx < toIndex)
678
- continue;
679
- const catalog = state.stageGateCatalog[stage];
680
- const gateIds = new Set([
681
- ...catalog.required,
682
- ...catalog.recommended,
683
- ...catalog.conditional,
684
- ...catalog.triggered,
685
- ...catalog.passed,
686
- ...catalog.blocked
687
- ]);
688
- for (const gateId of gateIds) {
689
- delete nextGuardEvidence[gateId];
690
- }
691
- }
692
- const nextStale = {};
693
- for (const [stage, marker] of Object.entries(state.staleStages)) {
694
- if (!marker)
695
- continue;
696
- const idx = stageToIndex.get(stage);
697
- if (idx === undefined || idx <= toIndex) {
698
- continue;
699
- }
700
- nextStale[stage] = marker;
701
- }
702
- for (const stage of invalidatedStages) {
703
- nextStale[stage] = {
704
- rewindId,
705
- reason,
706
- markedAt: nowIso
707
- };
708
- }
709
- const archivePath = rewindArchivePath(projectRoot, rewindId);
710
- const staleArtifacts = [];
711
- for (const stage of invalidatedStages) {
712
- const artifactFile = stageSchema(stage).artifactFile;
713
- const artifactPath = path.join(activeArtifactsPath(projectRoot), artifactFile);
714
- if (!(await exists(artifactPath))) {
715
- continue;
716
- }
717
- await ensureDir(archivePath);
718
- await ensureDir(path.join(archivePath, path.dirname(artifactFile)));
719
- await fs.copyFile(artifactPath, path.join(archivePath, artifactFile));
720
- const staleName = staleArtifactFileName(artifactFile);
721
- const stalePath = path.join(activeArtifactsPath(projectRoot), staleName);
722
- await fs.rm(stalePath, { force: true });
723
- await fs.rename(artifactPath, stalePath);
724
- staleArtifacts.push(staleName);
725
- }
726
- const rewindRecord = {
727
- id: rewindId,
728
- fromStage: state.currentStage,
729
- toStage: options.to,
730
- reason,
731
- timestamp: nowIso,
732
- invalidatedStages
733
- };
734
- const nextState = {
735
- ...state,
736
- currentStage: options.to,
737
- completedStages: nextCompletedStages,
738
- guardEvidence: nextGuardEvidence,
739
- stageGateCatalog: nextCatalog,
740
- staleStages: nextStale,
741
- rewinds: [...state.rewinds, rewindRecord]
742
- };
743
- await writeFlowState(projectRoot, nextState, { allowReset: true });
744
- const rewindLogEntry = {
745
- ...rewindRecord,
746
- track,
747
- runId: state.activeRunId,
748
- staleArtifacts
749
- };
750
- await ensureDir(path.dirname(rewindLogPath(projectRoot)));
751
- await fs.appendFile(rewindLogPath(projectRoot), `${JSON.stringify(rewindLogEntry)}\n`, "utf8");
752
- return {
753
- rewindId,
754
- from: state.currentStage,
755
- to: options.to,
756
- invalidatedStages,
757
- staleArtifacts,
758
- archivePath,
759
- nextState
760
- };
761
- }
762
- export async function acknowledgeStaleStage(projectRoot, stage) {
763
- await ensureRunSystem(projectRoot);
764
- const state = await readFlowState(projectRoot);
765
- const marker = state.staleStages[stage];
766
- if (!marker) {
767
- return {
768
- acknowledged: false,
769
- remaining: Object.keys(state.staleStages).filter((value) => isFlowStage(value))
770
- };
771
- }
772
- const nextStale = { ...state.staleStages };
773
- delete nextStale[stage];
774
- const nextState = {
775
- ...state,
776
- staleStages: nextStale
777
- };
778
- await writeFlowState(projectRoot, nextState, { allowReset: true });
779
- return {
780
- acknowledged: true,
781
- remaining: Object.keys(nextStale).filter((value) => isFlowStage(value))
782
- };
783
- }
784
597
  const KNOWLEDGE_SOFT_THRESHOLD = 50;
785
598
  async function readKnowledgeStats(projectRoot) {
786
599
  const knowledgePath = path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.21.0",
3
+ "version": "0.21.2",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {