@planu/cli 5.7.5 → 5.7.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/CHANGELOG.md +88 -0
- package/README.md +3 -3
- package/dist/.planu-build.json +1 -1
- package/dist/cli/commands/package-handoff.js +30 -5
- package/dist/config/compliance-profiles.json +36 -3
- package/dist/config/environment-schema.json +21 -0
- package/dist/config/hook-templates/planu-review-panel-trigger.sh +2 -3
- package/dist/config/hook-templates/planu-session-safeguard.sh +22 -15
- package/dist/config/official-sdd-tools.d.ts +1 -1
- package/dist/config/official-sdd-tools.js +11 -24
- package/dist/config/registries/hosts/codex.json +14 -30
- package/dist/config/registries/hosts/opencode.json +1 -6
- package/dist/config/server-instructions.js +0 -21
- package/dist/config/skill-templates/planu-context-assets.md +3 -6
- package/dist/config/skill-templates/planu-implement.md +2 -2
- package/dist/config/skill-templates/planu-multi-teammate-review.md +8 -12
- package/dist/config/skill-templates/planu-release.md +2 -7
- package/dist/config/skill-templates/planu-validate.md +3 -3
- package/dist/config/spec-templates/crud-rest-api/template.json +5 -1
- package/dist/config/spec-templates/file-upload-s3/template.json +5 -1
- package/dist/config/spec-templates/stripe-payments/template.json +5 -1
- package/dist/config/spec-templates/webhook-system/template.json +5 -1
- package/dist/config/subagent-templates/planu-challenger.md +4 -9
- package/dist/config/subagent-templates/planu-spec-implementer.md +2 -4
- package/dist/config/subagent-templates/planu-validator.md +5 -8
- package/dist/config/tool-groups.json +6 -144
- package/dist/engine/ambiguity-scorer.js +40 -1
- package/dist/engine/autopilot/bootstrap.js +1 -1
- package/dist/engine/code-impact-analyzer.js +2 -6
- package/dist/engine/context-orchestrator/index.js +2 -2
- package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
- package/dist/engine/dogfooding/runtime-gap-detector.js +2 -2
- package/dist/engine/evidence-gates/artifact-reader.d.ts +1 -0
- package/dist/engine/evidence-gates/artifact-reader.js +1 -1
- package/dist/engine/evidence-gates/evidence-autofill.d.ts +13 -5
- package/dist/engine/evidence-gates/evidence-autofill.js +106 -43
- package/dist/engine/evidence-gates/evidence-skeletons.js +3 -1
- package/dist/engine/evidence-gates/lifecycle-gate.js +7 -4
- package/dist/engine/evidence-index/index-builder.js +21 -4
- package/dist/engine/execution/job-runtime.js +1 -1
- package/dist/engine/execution/package-handoff-job-executor.d.ts +22 -0
- package/dist/engine/execution/package-handoff-job-executor.js +490 -0
- package/dist/engine/execution/validate-job-executor.js +20 -8
- package/dist/engine/git/exec-git.d.ts +4 -0
- package/dist/engine/git/exec-git.js +14 -0
- package/dist/engine/handoff-artifacts/implementation-review-reader.js +15 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -0
- package/dist/engine/handoff-artifacts/schemas.js +1 -0
- package/dist/engine/handoff-format.d.ts +36 -0
- package/dist/engine/handoff-format.js +314 -0
- package/dist/engine/handoff-packager.d.ts +5 -3
- package/dist/engine/handoff-packager.js +89 -32
- package/dist/engine/host-tool-filter.js +20 -49
- package/dist/engine/human-summary.js +14 -3
- package/dist/engine/implementation-contract/common.d.ts +8 -3
- package/dist/engine/implementation-contract/common.js +9 -6
- package/dist/engine/implementation-contract/evaluator.js +4 -6
- package/dist/engine/implementation-contract/renderer.js +18 -23
- package/dist/engine/project-health-checker.js +2 -2
- package/dist/engine/readiness-checker.js +43 -5
- package/dist/engine/reconcile/apply-changes.js +4 -0
- package/dist/engine/reconcile/propagate-mirrors.d.ts +3 -0
- package/dist/engine/reconcile/propagate-mirrors.js +311 -0
- package/dist/engine/self-healing/healer.js +12 -5
- package/dist/engine/self-healing/strategies/lint-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/lint-fix.js +4 -1
- package/dist/engine/self-healing/strategies/test-fix.d.ts +4 -3
- package/dist/engine/self-healing/strategies/test-fix.js +32 -14
- package/dist/engine/self-healing/strategies/typescript-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/typescript-fix.js +28 -10
- package/dist/engine/session/checkpoint-writer.js +8 -4
- package/dist/engine/session/session-tracker.d.ts +5 -16
- package/dist/engine/session/session-tracker.js +29 -26
- package/dist/engine/session-state/writer.js +3 -11
- package/dist/engine/skill-generator/workflow-skill-generator.d.ts +4 -2
- package/dist/engine/skill-generator/workflow-skill-generator.js +113 -38
- package/dist/engine/spec-format/bdd-parser.d.ts +1 -11
- package/dist/engine/spec-format/bdd-parser.js +40 -11
- package/dist/engine/spec-format/lean-spec-generator.js +32 -12
- package/dist/engine/spec-format/retired-scaffold.d.ts +3 -0
- package/dist/engine/spec-format/retired-scaffold.js +5 -0
- package/dist/engine/spec-format/technical-md-populator.d.ts +1 -0
- package/dist/engine/spec-format/technical-md-populator.js +27 -0
- package/dist/engine/spec-format/unified-spec-builder.d.ts +1 -0
- package/dist/engine/spec-format/unified-spec-builder.js +30 -20
- package/dist/engine/spec-grounding/contract.d.ts +3 -3
- package/dist/engine/spec-grounding/contract.js +65 -4
- package/dist/engine/spec-grounding/grounding-entry-parser.d.ts +5 -0
- package/dist/engine/spec-grounding/grounding-entry-parser.js +185 -0
- package/dist/engine/spec-quality/generic-output-gate.js +37 -3
- package/dist/engine/technical-enricher/index.js +4 -1
- package/dist/engine/type-safety-gate.js +3 -11
- package/dist/engine/universal-rules/catalog.js +0 -2
- package/dist/engine/universal-rules/rules/planu-workflow.js +1 -1
- package/dist/engine/validation/durable-validation.d.ts +3 -2
- package/dist/engine/validation/durable-validation.js +106 -40
- package/dist/engine/validation/validation-freshness.d.ts +3 -2
- package/dist/engine/validation/validation-freshness.js +22 -16
- package/dist/engine/validation/validation-worktree.js +34 -0
- package/dist/hosts/claude-code/ux/mcp-prompts.js +8 -8
- package/dist/index.js +3 -17
- package/dist/resources/process.js +38 -65
- package/dist/storage/session-state-store.js +2 -0
- package/dist/storage/spec-store.js +8 -1
- package/dist/tools/bump-spec-version.js +5 -5
- package/dist/tools/challenge-spec/scenarios-utils.js +2 -2
- package/dist/tools/configure-checkpoint-policy.js +3 -3
- package/dist/tools/create-spec-helpers.js +2 -5
- package/dist/tools/create-spec.js +21 -4
- package/dist/tools/facilitate.js +3 -6
- package/dist/tools/git/branch-ops.js +16 -4
- package/dist/tools/github-release-handler.js +1 -20
- package/dist/tools/init-project/agents-md-writer.js +6 -6
- package/dist/tools/init-project/claude-md-generator.d.ts +0 -1
- package/dist/tools/init-project/claude-md-generator.js +0 -35
- package/dist/tools/init-project/handler.js +7 -5
- package/dist/tools/init-project/per-client-files-writer.js +5 -5
- package/dist/tools/init-project/planu-workflow-generator.js +2 -13
- package/dist/tools/init-project/portable-index-reconciler.d.ts +2 -1
- package/dist/tools/init-project/portable-index-reconciler.js +7 -2
- package/dist/tools/init-project/rules-generator.d.ts +0 -11
- package/dist/tools/init-project/rules-generator.js +4 -85
- package/dist/tools/init-project/scaffold-writer.d.ts +0 -1
- package/dist/tools/init-project/scaffold-writer.js +1 -12
- package/dist/tools/init-project/skills-multi-teammate-review-writer.js +1 -1
- package/dist/tools/jobs/handlers.d.ts +0 -6
- package/dist/tools/jobs/handlers.js +13 -40
- package/dist/tools/list-specs.js +6 -21
- package/dist/tools/multi-teammate-review.js +1 -14
- package/dist/tools/package-handoff.d.ts +3 -2
- package/dist/tools/package-handoff.js +153 -427
- package/dist/tools/reconcile-session-safeguard-hook.js +22 -15
- package/dist/tools/register-platform-tools/design-stack-tools.js +3 -3
- package/dist/tools/register-sdd-tools.js +0 -23
- package/dist/tools/register-spec-tools/analysis-tools.js +22 -9
- package/dist/tools/register-spec-tools/core-spec-tools.js +115 -70
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +1 -18
- package/dist/tools/schemas/output-schemas.js +1 -11
- package/dist/tools/schemas/package-handoff-output-schema.d.ts +40 -0
- package/dist/tools/schemas/package-handoff-output-schema.js +68 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +1 -2
- package/dist/tools/schemas/validate-output-schema.js +3 -2
- package/dist/tools/session-checkpoint.js +2 -2
- package/dist/tools/skill-registry/index.d.ts +0 -1
- package/dist/tools/skill-registry/index.js +0 -1
- package/dist/tools/sync-spec-state-handler.d.ts +2 -2
- package/dist/tools/sync-spec-state-handler.js +23 -3
- package/dist/tools/tool-registry/core-tools.js +72 -164
- package/dist/tools/tool-registry/group-infra.js +2 -39
- package/dist/tools/tool-registry/group-quality-compliance.js +20 -15
- package/dist/tools/tool-registry-helpers.js +6 -2
- package/dist/tools/update-status/batch.js +17 -0
- package/dist/tools/update-status/dod-gates.js +14 -1
- package/dist/tools/update-status/done-receipt-verifier.d.ts +22 -0
- package/dist/tools/update-status/done-receipt-verifier.js +56 -26
- package/dist/tools/update-status/evidence-gate.js +10 -21
- package/dist/tools/update-status/file-sync.d.ts +1 -0
- package/dist/tools/update-status/file-sync.js +6 -0
- package/dist/tools/update-status/index.d.ts +6 -0
- package/dist/tools/update-status/index.js +39 -44
- package/dist/tools/update-status-actions.js +2 -7
- package/dist/tools/validate.js +34 -28
- package/dist/transports/oauth-validator.js +12 -1
- package/dist/transports/transport-factory.d.ts +2 -1
- package/dist/transports/transport-factory.js +19 -0
- package/dist/types/common/primitives.d.ts +2 -0
- package/dist/types/durable-job.d.ts +45 -0
- package/dist/types/durable-validation.d.ts +1 -1
- package/dist/types/evidence-autofill.d.ts +13 -1
- package/dist/types/handoff-artifacts.d.ts +2 -1
- package/dist/types/readiness.d.ts +8 -1
- package/dist/types/reconcile.d.ts +6 -0
- package/dist/types/skill-registry.d.ts +26 -1
- package/dist/types/spec/core.d.ts +5 -0
- package/dist/types/spec/inputs.d.ts +2 -2
- package/dist/types/spec-format.d.ts +11 -1
- package/dist/types/spec-grounding.d.ts +26 -0
- package/dist/types/transport.d.ts +1 -0
- package/dist/types/validation-receipt.d.ts +27 -0
- package/package.json +3 -2
- package/planu-plugin.json +13 -26
- package/src/i18n/messages/en.json +1 -1
- package/src/i18n/messages/es.json +1 -1
- package/src/i18n/messages/pt.json +1 -1
- package/dist/engine/skill-generator/conventions-hasher.d.ts +0 -7
- package/dist/engine/skill-generator/conventions-hasher.js +0 -24
- package/dist/engine/universal-rules/rules/agent-teams.d.ts +0 -3
- package/dist/engine/universal-rules/rules/agent-teams.js +0 -63
- package/dist/tools/clarify-requirements/multiple-choice.d.ts +0 -30
- package/dist/tools/clarify-requirements/multiple-choice.js +0 -306
- package/dist/tools/clarify-requirements/questions-context.d.ts +0 -9
- package/dist/tools/clarify-requirements/questions-context.js +0 -74
- package/dist/tools/clarify-requirements/questions.d.ts +0 -16
- package/dist/tools/clarify-requirements/questions.js +0 -96
- package/dist/tools/clarify-requirements.d.ts +0 -4
- package/dist/tools/clarify-requirements.js +0 -314
- package/dist/tools/code-graph-handler.d.ts +0 -6
- package/dist/tools/code-graph-handler.js +0 -72
- package/dist/tools/create-rule.d.ts +0 -4
- package/dist/tools/create-rule.js +0 -107
- package/dist/tools/feedback-handler.d.ts +0 -7
- package/dist/tools/feedback-handler.js +0 -150
- package/dist/tools/semantic-search-handler.d.ts +0 -7
- package/dist/tools/semantic-search-handler.js +0 -71
- package/dist/tools/skill-registry/search.d.ts +0 -11
- package/dist/tools/skill-registry/search.js +0 -159
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +0 -19
- package/dist/tools/tool-registry/deprecated-stubs.js +0 -65
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// engine/self-healing/strategies/lint-fix.ts — SPEC-474: ESLint auto-fix strategy
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
4
5
|
const execFileAsync = promisify(execFile);
|
|
5
6
|
/**
|
|
6
7
|
* Runs eslint --fix on the project and reports what was fixed.
|
|
7
8
|
*/
|
|
8
|
-
export async function applyLintFix(projectPath, _details) {
|
|
9
|
+
export async function applyLintFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
9
10
|
try {
|
|
10
11
|
await execFileAsync('pnpm', ['eslint', '--fix', 'src/', 'tests/', '--ext', '.ts'], {
|
|
11
12
|
cwd: projectPath,
|
|
13
|
+
timeout: timeoutMs,
|
|
14
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
12
15
|
});
|
|
13
16
|
return {
|
|
14
17
|
fixed: true,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { StrategyResult } from '../../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* Identifies failing tests from
|
|
4
|
-
* Does NOT modify test files — test logic changes
|
|
3
|
+
* Identifies failing tests from the project's test command output and reports
|
|
4
|
+
* what needs manual review. Does NOT modify test files — test logic changes
|
|
5
|
+
* require human judgment.
|
|
5
6
|
*/
|
|
6
|
-
export declare function applyTestFix(projectPath: string, _details: string): Promise<StrategyResult>;
|
|
7
|
+
export declare function applyTestFix(projectPath: string, _details: string, timeoutMs?: number): Promise<StrategyResult>;
|
|
7
8
|
//# sourceMappingURL=test-fix.d.ts.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// engine/self-healing/strategies/test-fix.ts — SPEC-474: Test fix strategy
|
|
2
|
-
import {
|
|
2
|
+
import { detectPackageManager } from '../../project-dna/stack-detector.js';
|
|
3
|
+
import { runAbortableProcess } from '../../abortable-process-runner.js';
|
|
4
|
+
import { getRuntimePolicy } from '../../runtime-policy.js';
|
|
5
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
3
6
|
/**
|
|
4
7
|
* Parses failing test entries from vitest output.
|
|
5
8
|
* Looks for:
|
|
@@ -42,25 +45,40 @@ function parseFailingTests(output) {
|
|
|
42
45
|
}
|
|
43
46
|
return results;
|
|
44
47
|
}
|
|
48
|
+
function testCommandFor(packageManager) {
|
|
49
|
+
switch (packageManager) {
|
|
50
|
+
case 'yarn':
|
|
51
|
+
return { executable: 'yarn', args: ['test'] };
|
|
52
|
+
case 'npm':
|
|
53
|
+
return { executable: 'npm', args: ['test'] };
|
|
54
|
+
case 'bun':
|
|
55
|
+
return { executable: 'bun', args: ['run', 'test'] };
|
|
56
|
+
default:
|
|
57
|
+
return { executable: 'pnpm', args: ['test'] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
45
60
|
/**
|
|
46
|
-
* Identifies failing tests from
|
|
47
|
-
* Does NOT modify test files — test logic changes
|
|
61
|
+
* Identifies failing tests from the project's test command output and reports
|
|
62
|
+
* what needs manual review. Does NOT modify test files — test logic changes
|
|
63
|
+
* require human judgment.
|
|
48
64
|
*/
|
|
49
|
-
export function applyTestFix(projectPath, _details) {
|
|
50
|
-
return
|
|
65
|
+
export async function applyTestFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
66
|
+
return runTestDiagnostic(await captureTestOutput(projectPath, timeoutMs));
|
|
51
67
|
}
|
|
52
|
-
function captureTestOutput(projectPath) {
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
async function captureTestOutput(projectPath, timeoutMs) {
|
|
69
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
70
|
+
const { executable, args } = testCommandFor(packageManager);
|
|
71
|
+
const result = await runAbortableProcess(executable, args, {
|
|
72
|
+
cwd: projectPath,
|
|
73
|
+
timeoutMs,
|
|
74
|
+
maxBufferBytes: getRuntimePolicy().qualityAssurance.maximumOutputBufferBytes,
|
|
75
|
+
});
|
|
76
|
+
if (result.status === 0) {
|
|
55
77
|
return null;
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
const errObj = err;
|
|
59
|
-
return errObj.stdout ?? errObj.stderr ?? errObj.message ?? '';
|
|
60
|
-
}
|
|
79
|
+
return [result.stdout, result.stderr].join('\n');
|
|
61
80
|
}
|
|
62
|
-
function runTestDiagnostic(
|
|
63
|
-
const output = captureTestOutput(projectPath);
|
|
81
|
+
function runTestDiagnostic(output) {
|
|
64
82
|
if (output === null) {
|
|
65
83
|
return { fixed: true, reason: 'All tests pass — no failures detected', fixedIssues: [] };
|
|
66
84
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { StrategyResult } from '../../../types/index.js';
|
|
2
|
-
export declare function applyTypescriptFix(projectPath: string, _details: string): Promise<StrategyResult>;
|
|
2
|
+
export declare function applyTypescriptFix(projectPath: string, _details: string, timeoutMs?: number): Promise<StrategyResult>;
|
|
3
3
|
//# sourceMappingURL=typescript-fix.d.ts.map
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// engine/self-healing/strategies/typescript-fix.ts — SPEC-474: TypeScript fix strategy
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
3
|
import { join } from 'node:path';
|
|
4
|
+
import { detectPackageManager } from '../../project-dna/stack-detector.js';
|
|
5
|
+
import { runAbortableProcess } from '../../abortable-process-runner.js';
|
|
6
|
+
import { getRuntimePolicy } from '../../runtime-policy.js';
|
|
7
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
5
8
|
/**
|
|
6
9
|
* Parses TS6133 (unused import) errors from tsc output.
|
|
7
10
|
* Format: path/file.ts(10,5): error TS6133: 'foo' is declared but its value is never read.
|
|
@@ -94,22 +97,37 @@ async function fixFileUnusedImports(projectPath, filePath, entries) {
|
|
|
94
97
|
}
|
|
95
98
|
return { fixed: fixedDescriptions, skipped };
|
|
96
99
|
}
|
|
100
|
+
function typecheckCommandFor(packageManager) {
|
|
101
|
+
switch (packageManager) {
|
|
102
|
+
case 'yarn':
|
|
103
|
+
return { executable: 'yarn', args: ['typecheck'] };
|
|
104
|
+
case 'npm':
|
|
105
|
+
return { executable: 'npm', args: ['run', 'typecheck'] };
|
|
106
|
+
case 'bun':
|
|
107
|
+
return { executable: 'bun', args: ['run', 'typecheck'] };
|
|
108
|
+
default:
|
|
109
|
+
return { executable: 'pnpm', args: ['typecheck'] };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
/**
|
|
98
113
|
* Attempts to fix TypeScript errors by parsing tsc output and applying safe fixes:
|
|
99
114
|
* - TS6133: removes unused imports when the pattern is unambiguous
|
|
100
115
|
*/
|
|
101
|
-
function captureTypecheckOutput(projectPath) {
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
async function captureTypecheckOutput(projectPath, timeoutMs) {
|
|
117
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
118
|
+
const { executable, args } = typecheckCommandFor(packageManager);
|
|
119
|
+
const result = await runAbortableProcess(executable, args, {
|
|
120
|
+
cwd: projectPath,
|
|
121
|
+
timeoutMs,
|
|
122
|
+
maxBufferBytes: getRuntimePolicy().qualityAssurance.maximumOutputBufferBytes,
|
|
123
|
+
});
|
|
124
|
+
if (result.status === 0) {
|
|
104
125
|
return null;
|
|
105
126
|
}
|
|
106
|
-
|
|
107
|
-
const errObj = err;
|
|
108
|
-
return errObj.stdout ?? errObj.stderr ?? errObj.message ?? '';
|
|
109
|
-
}
|
|
127
|
+
return [result.stdout, result.stderr].join('\n');
|
|
110
128
|
}
|
|
111
|
-
export async function applyTypescriptFix(projectPath, _details) {
|
|
112
|
-
const output = captureTypecheckOutput(projectPath);
|
|
129
|
+
export async function applyTypescriptFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
130
|
+
const output = await captureTypecheckOutput(projectPath, timeoutMs);
|
|
113
131
|
if (output === null) {
|
|
114
132
|
return { fixed: true, reason: 'No TypeScript errors found', fixedIssues: [] };
|
|
115
133
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Fire-and-forget safe — never throws to caller.
|
|
4
4
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { getSessionTracker } from './session-tracker.js';
|
|
7
7
|
const MAX_DISPLAYED_CALLS = 5;
|
|
8
8
|
const MAX_DISPLAYED_ERRORS = 3;
|
|
9
9
|
const MAX_DISPLAYED_FILES = 10;
|
|
@@ -83,9 +83,13 @@ export async function writeCheckpoint(projectPath, snapshot) {
|
|
|
83
83
|
* Call this from safe-handler.ts after each tool response.
|
|
84
84
|
*/
|
|
85
85
|
export function maybeWriteCheckpoint(toolName, success, projectPath) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
if (projectPath === undefined) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const tracker = getSessionTracker(projectPath);
|
|
90
|
+
tracker.recordCall(toolName, success);
|
|
91
|
+
if (tracker.shouldCheckpoint()) {
|
|
92
|
+
void writeCheckpoint(projectPath, tracker.getSnapshot()).catch(() => {
|
|
89
93
|
/* resilient — never affects tool response */
|
|
90
94
|
});
|
|
91
95
|
}
|
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
import type { AutoCheckpointPolicy, AutoSessionSnapshot } from '../../types/index.js';
|
|
2
|
-
declare class SessionTracker {
|
|
2
|
+
export declare class SessionTracker {
|
|
3
3
|
private callCount;
|
|
4
4
|
private readonly lastCalls;
|
|
5
5
|
private readonly modifiedFiles;
|
|
6
6
|
private readonly errors;
|
|
7
|
-
private policy;
|
|
8
|
-
/** Record a tool call. Appends to lastCalls (capped at MAX_LAST_CALLS). */
|
|
9
7
|
recordCall(tool: string, success: boolean): void;
|
|
10
|
-
/** Record a file path as modified during this session. */
|
|
11
8
|
recordFile(path: string): void;
|
|
12
|
-
/** Record an error message (capped at MAX_ERRORS). */
|
|
13
9
|
recordError(msg: string): void;
|
|
14
|
-
/**
|
|
15
|
-
* Returns true when a checkpoint should be written:
|
|
16
|
-
* policy is enabled, callCount > 0, and callCount is a multiple of threshold.
|
|
17
|
-
*/
|
|
18
10
|
shouldCheckpoint(): boolean;
|
|
19
|
-
/** Return a compact snapshot of the current session state. */
|
|
20
11
|
getSnapshot(): AutoSessionSnapshot;
|
|
21
|
-
/** Update the checkpoint policy (partial update — unspecified fields keep current values). */
|
|
22
12
|
setPolicy(p: Partial<AutoCheckpointPolicy>): void;
|
|
23
|
-
/** Return the current checkpoint policy. */
|
|
24
13
|
getPolicy(): AutoCheckpointPolicy;
|
|
25
|
-
/** Reset the tracker state (useful for testing). */
|
|
26
14
|
reset(): void;
|
|
27
15
|
}
|
|
28
|
-
|
|
29
|
-
export declare
|
|
30
|
-
export
|
|
16
|
+
export declare function getSessionTracker(projectPath: string): SessionTracker;
|
|
17
|
+
export declare function setCheckpointPolicy(p: Partial<AutoCheckpointPolicy>): void;
|
|
18
|
+
export declare function getCheckpointPolicy(): AutoCheckpointPolicy;
|
|
19
|
+
export declare function resetSessionTrackers(): void;
|
|
31
20
|
//# sourceMappingURL=session-tracker.d.ts.map
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
// engine/session/session-tracker.ts — In-memory call counter and file modification tracker
|
|
2
|
-
// for auto session checkpointing (SPEC-563).
|
|
3
1
|
const DEFAULT_THRESHOLD = 10;
|
|
4
2
|
const MAX_LAST_CALLS = 10;
|
|
5
3
|
const MAX_ERRORS = 20;
|
|
6
|
-
|
|
4
|
+
let sharedPolicy = { threshold: DEFAULT_THRESHOLD, enabled: true };
|
|
5
|
+
export class SessionTracker {
|
|
7
6
|
callCount = 0;
|
|
8
7
|
lastCalls = [];
|
|
9
8
|
modifiedFiles = new Set();
|
|
10
9
|
errors = [];
|
|
11
|
-
policy = { threshold: DEFAULT_THRESHOLD, enabled: true };
|
|
12
|
-
/** Record a tool call. Appends to lastCalls (capped at MAX_LAST_CALLS). */
|
|
13
10
|
recordCall(tool, success) {
|
|
14
11
|
this.callCount++;
|
|
15
12
|
this.lastCalls.push({ tool, success, timestamp: new Date().toISOString() });
|
|
@@ -17,25 +14,18 @@ class SessionTracker {
|
|
|
17
14
|
this.lastCalls.shift();
|
|
18
15
|
}
|
|
19
16
|
}
|
|
20
|
-
/** Record a file path as modified during this session. */
|
|
21
17
|
recordFile(path) {
|
|
22
18
|
this.modifiedFiles.add(path);
|
|
23
19
|
}
|
|
24
|
-
/** Record an error message (capped at MAX_ERRORS). */
|
|
25
20
|
recordError(msg) {
|
|
26
21
|
if (this.errors.length >= MAX_ERRORS) {
|
|
27
22
|
this.errors.shift();
|
|
28
23
|
}
|
|
29
24
|
this.errors.push(msg);
|
|
30
25
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Returns true when a checkpoint should be written:
|
|
33
|
-
* policy is enabled, callCount > 0, and callCount is a multiple of threshold.
|
|
34
|
-
*/
|
|
35
26
|
shouldCheckpoint() {
|
|
36
|
-
return (
|
|
27
|
+
return (sharedPolicy.enabled && this.callCount > 0 && this.callCount % sharedPolicy.threshold === 0);
|
|
37
28
|
}
|
|
38
|
-
/** Return a compact snapshot of the current session state. */
|
|
39
29
|
getSnapshot() {
|
|
40
30
|
return {
|
|
41
31
|
callCount: this.callCount,
|
|
@@ -44,28 +34,41 @@ class SessionTracker {
|
|
|
44
34
|
errors: [...this.errors],
|
|
45
35
|
};
|
|
46
36
|
}
|
|
47
|
-
/** Update the checkpoint policy (partial update — unspecified fields keep current values). */
|
|
48
37
|
setPolicy(p) {
|
|
49
|
-
|
|
50
|
-
this.policy = { ...this.policy, threshold: Math.max(1, Math.round(p.threshold)) };
|
|
51
|
-
}
|
|
52
|
-
if (p.enabled !== undefined) {
|
|
53
|
-
this.policy = { ...this.policy, enabled: p.enabled };
|
|
54
|
-
}
|
|
38
|
+
setCheckpointPolicy(p);
|
|
55
39
|
}
|
|
56
|
-
/** Return the current checkpoint policy. */
|
|
57
40
|
getPolicy() {
|
|
58
|
-
return
|
|
41
|
+
return getCheckpointPolicy();
|
|
59
42
|
}
|
|
60
|
-
/** Reset the tracker state (useful for testing). */
|
|
61
43
|
reset() {
|
|
62
44
|
this.callCount = 0;
|
|
63
45
|
this.lastCalls.splice(0);
|
|
64
46
|
this.modifiedFiles.clear();
|
|
65
47
|
this.errors.splice(0);
|
|
66
|
-
this.policy = { threshold: DEFAULT_THRESHOLD, enabled: true };
|
|
67
48
|
}
|
|
68
49
|
}
|
|
69
|
-
|
|
70
|
-
export
|
|
50
|
+
const trackers = new Map();
|
|
51
|
+
export function getSessionTracker(projectPath) {
|
|
52
|
+
let tracker = trackers.get(projectPath);
|
|
53
|
+
if (!tracker) {
|
|
54
|
+
tracker = new SessionTracker();
|
|
55
|
+
trackers.set(projectPath, tracker);
|
|
56
|
+
}
|
|
57
|
+
return tracker;
|
|
58
|
+
}
|
|
59
|
+
export function setCheckpointPolicy(p) {
|
|
60
|
+
if (p.threshold !== undefined) {
|
|
61
|
+
sharedPolicy = { ...sharedPolicy, threshold: Math.max(1, Math.round(p.threshold)) };
|
|
62
|
+
}
|
|
63
|
+
if (p.enabled !== undefined) {
|
|
64
|
+
sharedPolicy = { ...sharedPolicy, enabled: p.enabled };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function getCheckpointPolicy() {
|
|
68
|
+
return { ...sharedPolicy };
|
|
69
|
+
}
|
|
70
|
+
export function resetSessionTrackers() {
|
|
71
|
+
trackers.clear();
|
|
72
|
+
sharedPolicy = { threshold: DEFAULT_THRESHOLD, enabled: true };
|
|
73
|
+
}
|
|
71
74
|
//# sourceMappingURL=session-tracker.js.map
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// engine/session-state/writer.ts — SPEC-633: Write session.json universal state file
|
|
3
3
|
import { readFile, mkdir } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { execSync } from 'node:child_process';
|
|
6
5
|
import { stableJsonStringify, stableWriteGeneratedFile, stripRootUpdatedAt, } from '../generated-artifact-writer.js';
|
|
6
|
+
import { execGitCapture } from '../git/exec-git.js';
|
|
7
7
|
// eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1699
|
|
8
8
|
import { projectRuntimeDir } from '../../storage/storage-layout.js';
|
|
9
9
|
const ROOT_SESSION = 'session.json';
|
|
@@ -18,11 +18,7 @@ export function rootFile(projectPath) {
|
|
|
18
18
|
}
|
|
19
19
|
function getModifiedFiles(projectPath) {
|
|
20
20
|
try {
|
|
21
|
-
const out =
|
|
22
|
-
cwd: projectPath,
|
|
23
|
-
encoding: 'utf-8',
|
|
24
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
25
|
-
});
|
|
21
|
+
const out = execGitCapture('git diff --name-only HEAD 2>/dev/null', projectPath);
|
|
26
22
|
return out.trim().split('\n').filter(Boolean).slice(0, 10);
|
|
27
23
|
}
|
|
28
24
|
catch {
|
|
@@ -31,11 +27,7 @@ function getModifiedFiles(projectPath) {
|
|
|
31
27
|
}
|
|
32
28
|
function getBranch(projectPath) {
|
|
33
29
|
try {
|
|
34
|
-
const out =
|
|
35
|
-
cwd: projectPath,
|
|
36
|
-
encoding: 'utf-8',
|
|
37
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
-
});
|
|
30
|
+
const out = execGitCapture('git rev-parse --abbrev-ref HEAD 2>/dev/null', projectPath);
|
|
39
31
|
return out.trim() || null;
|
|
40
32
|
}
|
|
41
33
|
catch {
|
|
@@ -3,8 +3,10 @@ import type { WorkflowSkillGenerationOptions, WorkflowSkillGenerationResult } fr
|
|
|
3
3
|
export type { WorkflowSkillGenerationOptions, WorkflowSkillGenerationResult, } from '../../types/skill-registry.js';
|
|
4
4
|
/**
|
|
5
5
|
* Generate all 22 workflow skill files for the given project.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Refreshes a pristine copy when Planu's shipped body changes; never touches
|
|
7
|
+
* a user-authored or user-edited file (SPEC-1457).
|
|
8
|
+
* Writes registry entries via writeSkillRegistryEntry (SPEC-680) only when a
|
|
9
|
+
* file is actually rewritten.
|
|
8
10
|
*/
|
|
9
11
|
export declare function generateWorkflowSkills(projectPath: string, knowledge: ProjectKnowledge, options?: WorkflowSkillGenerationOptions): Promise<WorkflowSkillGenerationResult>;
|
|
10
12
|
//# sourceMappingURL=workflow-skill-generator.d.ts.map
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
import { technologyValue } from '../technology-registry.js';
|
|
2
|
-
// src/engine/skill-generator/workflow-skill-generator.ts — SPEC-685
|
|
2
|
+
// src/engine/skill-generator/workflow-skill-generator.ts — SPEC-685, refreshed by SPEC-1457
|
|
3
3
|
// Generates all 22 workflow skill files for a project.
|
|
4
4
|
// Delegates placeholder substitution to adaptSkillContent (SPEC-682 — DRY).
|
|
5
5
|
// Registers each skill via writeSkillRegistryEntry (SPEC-680).
|
|
6
6
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { adaptSkillContent } from '../skill-adapter/index.js';
|
|
9
10
|
// eslint-disable-next-line no-restricted-imports -- grandfathered layer violation, remediation SPEC-1661 SPEC-1662 SPEC-1663
|
|
10
11
|
import { writeSkillRegistryEntry } from '../../storage/skill-registry-storage.js';
|
|
11
12
|
import { WORKFLOW_SKILL_DEFINITIONS } from './skill-definitions.js';
|
|
12
|
-
import { hashConventions } from './conventions-hasher.js';
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Constants
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
const CLAUDE_SKILLS_REL = '.claude/skills';
|
|
17
|
-
const HASH_MARKER_PREFIX = '<!-- planu-
|
|
17
|
+
const HASH_MARKER_PREFIX = '<!-- planu-skill-hash: ';
|
|
18
18
|
const HASH_MARKER_SUFFIX = ' -->';
|
|
19
|
+
const LEGACY_HASH_MARKER_PREFIX = '<!-- planu-conventions-hash: ';
|
|
20
|
+
const MARKER_SCAN_WINDOW = 5;
|
|
21
|
+
const HEX_HASH_PATTERN = /^[0-9a-f]{64}$/;
|
|
22
|
+
const LEGACY_HEX_HASH_PATTERN = /^[0-9a-f]{16}$/;
|
|
19
23
|
// ---------------------------------------------------------------------------
|
|
20
24
|
// Helpers
|
|
21
25
|
// ---------------------------------------------------------------------------
|
|
22
|
-
/**
|
|
26
|
+
/** SHA-256 hex digest of the exact bytes of the rendered skill content. */
|
|
27
|
+
function digestContent(content) {
|
|
28
|
+
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
29
|
+
}
|
|
30
|
+
/** Build the full skill markdown with the content-bound hash marker on line 2. */
|
|
23
31
|
function buildSkillContent(adaptedBody, hash) {
|
|
24
32
|
const marker = `${HASH_MARKER_PREFIX}${hash}${HASH_MARKER_SUFFIX}`;
|
|
25
|
-
// Insert hash marker as second line (after the first line of the body)
|
|
26
33
|
const lines = adaptedBody.split('\n');
|
|
27
34
|
if (lines.length < 2) {
|
|
28
35
|
return adaptedBody + '\n' + marker + '\n';
|
|
@@ -30,16 +37,58 @@ function buildSkillContent(adaptedBody, hash) {
|
|
|
30
37
|
lines.splice(1, 0, marker);
|
|
31
38
|
return lines.join('\n');
|
|
32
39
|
}
|
|
33
|
-
/**
|
|
34
|
-
function
|
|
40
|
+
/** Rendered content with the marker line at `lineIndex` removed, for the pristine digest check. */
|
|
41
|
+
function contentWithoutMarkerLine(content, lineIndex) {
|
|
42
|
+
const lines = content.split('\n');
|
|
43
|
+
lines.splice(lineIndex, 1);
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
function extractMarkerValue(line, prefix) {
|
|
47
|
+
if (!line.startsWith(prefix) || !line.endsWith(HASH_MARKER_SUFFIX)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return line.slice(prefix.length, -HASH_MARKER_SUFFIX.length);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fail-closed marker reader: any duplicate marker, mixed marker formats, or a
|
|
54
|
+
* malformed hash value is reported as 'invalid' so the caller treats the file
|
|
55
|
+
* as user-modified rather than risk overwriting an edit.
|
|
56
|
+
*/
|
|
57
|
+
function readMarker(content) {
|
|
35
58
|
const lines = content.split('\n');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
const window = lines.slice(0, MARKER_SCAN_WINDOW);
|
|
60
|
+
let currentHash = '';
|
|
61
|
+
let currentLineIndex = -1;
|
|
62
|
+
let currentCount = 0;
|
|
63
|
+
let legacyHash = '';
|
|
64
|
+
let legacyCount = 0;
|
|
65
|
+
for (let lineIndex = 0; lineIndex < window.length; lineIndex++) {
|
|
66
|
+
const line = window[lineIndex] ?? '';
|
|
67
|
+
const currentValue = extractMarkerValue(line, HASH_MARKER_PREFIX);
|
|
68
|
+
if (currentValue !== null) {
|
|
69
|
+
currentCount += 1;
|
|
70
|
+
currentHash = currentValue;
|
|
71
|
+
currentLineIndex = lineIndex;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const legacyValue = extractMarkerValue(line, LEGACY_HASH_MARKER_PREFIX);
|
|
75
|
+
if (legacyValue !== null) {
|
|
76
|
+
legacyCount += 1;
|
|
77
|
+
legacyHash = legacyValue;
|
|
40
78
|
}
|
|
41
79
|
}
|
|
42
|
-
|
|
80
|
+
if (currentCount > 1 || legacyCount > 1 || (currentCount === 1 && legacyCount === 1)) {
|
|
81
|
+
return { kind: 'invalid' };
|
|
82
|
+
}
|
|
83
|
+
if (currentCount === 1) {
|
|
84
|
+
return HEX_HASH_PATTERN.test(currentHash)
|
|
85
|
+
? { kind: 'current', hash: currentHash, lineIndex: currentLineIndex }
|
|
86
|
+
: { kind: 'invalid' };
|
|
87
|
+
}
|
|
88
|
+
if (legacyCount === 1) {
|
|
89
|
+
return LEGACY_HEX_HASH_PATTERN.test(legacyHash) ? { kind: 'legacy' } : { kind: 'invalid' };
|
|
90
|
+
}
|
|
91
|
+
return { kind: 'absent' };
|
|
43
92
|
}
|
|
44
93
|
/** Apply fallback values when knowledge fields are null. */
|
|
45
94
|
function applyFallbacks(knowledge) {
|
|
@@ -53,55 +102,81 @@ function applyFallbacks(knowledge) {
|
|
|
53
102
|
language: knowledge.language || technologyValue('technology-typescript-969545'),
|
|
54
103
|
};
|
|
55
104
|
}
|
|
105
|
+
async function decideForFile(destPath, newDigest, force) {
|
|
106
|
+
let existingContent;
|
|
107
|
+
try {
|
|
108
|
+
existingContent = await readFile(destPath, 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
const code = err.code;
|
|
112
|
+
if (code === 'ENOENT') {
|
|
113
|
+
return { action: 'write' };
|
|
114
|
+
}
|
|
115
|
+
return { action: 'skip-error', message: err instanceof Error ? err.message : String(err) };
|
|
116
|
+
}
|
|
117
|
+
const marker = readMarker(existingContent);
|
|
118
|
+
switch (marker.kind) {
|
|
119
|
+
case 'invalid':
|
|
120
|
+
case 'absent':
|
|
121
|
+
return { action: 'skip-user-modified' };
|
|
122
|
+
case 'legacy':
|
|
123
|
+
return { action: 'write' };
|
|
124
|
+
case 'current': {
|
|
125
|
+
const onDiskDigest = digestContent(contentWithoutMarkerLine(existingContent, marker.lineIndex));
|
|
126
|
+
if (onDiskDigest !== marker.hash) {
|
|
127
|
+
return { action: 'skip-user-modified' };
|
|
128
|
+
}
|
|
129
|
+
if (force || marker.hash !== newDigest) {
|
|
130
|
+
return { action: 'write' };
|
|
131
|
+
}
|
|
132
|
+
return { action: 'skip-unchanged' };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
56
136
|
// ---------------------------------------------------------------------------
|
|
57
137
|
// Main export
|
|
58
138
|
// ---------------------------------------------------------------------------
|
|
59
139
|
/**
|
|
60
140
|
* Generate all 22 workflow skill files for the given project.
|
|
61
|
-
*
|
|
62
|
-
*
|
|
141
|
+
* Refreshes a pristine copy when Planu's shipped body changes; never touches
|
|
142
|
+
* a user-authored or user-edited file (SPEC-1457).
|
|
143
|
+
* Writes registry entries via writeSkillRegistryEntry (SPEC-680) only when a
|
|
144
|
+
* file is actually rewritten.
|
|
63
145
|
*/
|
|
64
146
|
export async function generateWorkflowSkills(projectPath, knowledge, options = {}) {
|
|
65
147
|
const targets = options.targets ?? ['claude'];
|
|
66
148
|
const force = options.force ?? false;
|
|
67
149
|
// Apply fallbacks so placeholders always resolve
|
|
68
150
|
const resolvedKnowledge = applyFallbacks(knowledge);
|
|
69
|
-
// Compute current conventions hash (same for all skills in this run)
|
|
70
|
-
const currentHash = hashConventions(resolvedKnowledge);
|
|
71
151
|
// Ensure output directories exist
|
|
72
152
|
const claudeSkillsDir = join(projectPath, CLAUDE_SKILLS_REL);
|
|
73
153
|
await mkdir(claudeSkillsDir, { recursive: true });
|
|
74
154
|
const written = [];
|
|
75
155
|
const skipped = [];
|
|
156
|
+
const userModified = [];
|
|
76
157
|
const errors = {};
|
|
77
158
|
for (const def of WORKFLOW_SKILL_DEFINITIONS) {
|
|
78
159
|
try {
|
|
79
160
|
const skillFileName = `${def.name}.md`;
|
|
80
161
|
const destPath = join(claudeSkillsDir, skillFileName);
|
|
81
|
-
// --- Idempotency check ---
|
|
82
|
-
if (!force) {
|
|
83
|
-
let existingContent = null;
|
|
84
|
-
try {
|
|
85
|
-
existingContent = await readFile(destPath, 'utf-8');
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
// File does not exist — will write
|
|
89
|
-
}
|
|
90
|
-
if (existingContent !== null) {
|
|
91
|
-
const existingHash = extractExistingHash(existingContent);
|
|
92
|
-
// Scenario 9: missing hash marker → treat as foreign, overwrite
|
|
93
|
-
// Scenario 3: same hash → skip
|
|
94
|
-
if (existingHash !== null && existingHash === currentHash) {
|
|
95
|
-
skipped.push(def.name);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
// existingHash === null (missing marker) → fall through and overwrite
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
162
|
// --- Adapt content via SPEC-682 (DRY — no duplicated placeholder logic) ---
|
|
102
163
|
const { adapted } = adaptSkillContent(def.templateBody, resolvedKnowledge);
|
|
164
|
+
const newDigest = digestContent(adapted);
|
|
165
|
+
const decision = await decideForFile(destPath, newDigest, force);
|
|
166
|
+
if (decision.action === 'skip-unchanged') {
|
|
167
|
+
skipped.push(def.name);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (decision.action === 'skip-user-modified') {
|
|
171
|
+
userModified.push(def.name);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (decision.action === 'skip-error') {
|
|
175
|
+
errors[def.name] = decision.message;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
103
178
|
// --- Build final content with hash marker ---
|
|
104
|
-
const finalContent = buildSkillContent(adapted,
|
|
179
|
+
const finalContent = buildSkillContent(adapted, newDigest);
|
|
105
180
|
// --- Write .claude/skills/{name}.md ---
|
|
106
181
|
if (targets.includes('claude')) {
|
|
107
182
|
await writeFile(destPath, finalContent, 'utf-8');
|
|
@@ -121,6 +196,6 @@ export async function generateWorkflowSkills(projectPath, knowledge, options = {
|
|
|
121
196
|
errors[def.name] = err instanceof Error ? err.message : String(err);
|
|
122
197
|
}
|
|
123
198
|
}
|
|
124
|
-
return { written, skipped, errors };
|
|
199
|
+
return { written, skipped, userModified, errors };
|
|
125
200
|
}
|
|
126
201
|
//# sourceMappingURL=workflow-skill-generator.js.map
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import type { BddScenario, BddStep, LeanCriterion } from '../../types/index.js';
|
|
2
2
|
export type { BddScenario, BddStep };
|
|
3
|
-
|
|
4
|
-
* Parses Gherkin-style blocks from a markdown string.
|
|
5
|
-
*
|
|
6
|
-
* Recognises:
|
|
7
|
-
* Scenario: <title>
|
|
8
|
-
* Given ...
|
|
9
|
-
* When ...
|
|
10
|
-
* Then ...
|
|
11
|
-
* And ...
|
|
12
|
-
* But ...
|
|
13
|
-
*/
|
|
3
|
+
export declare function extractInlineTestPaths(text: string): string[];
|
|
14
4
|
export declare function parseBddScenarios(markdown: string): BddScenario[];
|
|
15
5
|
/**
|
|
16
6
|
* Converts an array of BddScenario objects into YAML lines for the frontmatter.
|