@niujiajie/freespec 0.0.0-stage → 1.15.0
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/LICENSE +22 -0
- package/README.md +58 -2
- package/bin/freespec.js +5 -0
- package/dist/cli/index.d.ts +25 -0
- package/dist/cli/index.js +359 -0
- package/dist/commands/shared-output.d.ts +18 -0
- package/dist/commands/shared-output.js +61 -0
- package/dist/commands/workflow/default-schema.d.ts +2 -0
- package/dist/commands/workflow/default-schema.js +4 -0
- package/dist/commands/workflow/index.d.ts +13 -0
- package/dist/commands/workflow/index.js +10 -0
- package/dist/commands/workflow/instructions.d.ts +39 -0
- package/dist/commands/workflow/instructions.js +565 -0
- package/dist/commands/workflow/new-change.d.ts +19 -0
- package/dist/commands/workflow/new-change.js +135 -0
- package/dist/commands/workflow/shared.d.ts +94 -0
- package/dist/commands/workflow/shared.js +169 -0
- package/dist/commands/workflow/status.d.ts +18 -0
- package/dist/commands/workflow/status.js +201 -0
- package/dist/core/artifact-graph/graph.d.ts +72 -0
- package/dist/core/artifact-graph/graph.js +165 -0
- package/dist/core/artifact-graph/index.d.ts +9 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +208 -0
- package/dist/core/artifact-graph/instruction-loader.js +268 -0
- package/dist/core/artifact-graph/outputs.d.ts +19 -0
- package/dist/core/artifact-graph/outputs.js +351 -0
- package/dist/core/artifact-graph/resolver.d.ts +89 -0
- package/dist/core/artifact-graph/resolver.js +342 -0
- package/dist/core/artifact-graph/schema.d.ts +27 -0
- package/dist/core/artifact-graph/schema.js +160 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +31 -0
- package/dist/core/artifact-graph/types.d.ts +40 -0
- package/dist/core/artifact-graph/types.js +59 -0
- package/dist/core/available-tools.d.ts +18 -0
- package/dist/core/available-tools.js +49 -0
- package/dist/core/change-layout.d.ts +17 -0
- package/dist/core/change-layout.js +30 -0
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/schema.d.ts +23 -0
- package/dist/core/change-metadata/schema.js +46 -0
- package/dist/core/change-status-policy.d.ts +49 -0
- package/dist/core/change-status-policy.js +50 -0
- package/dist/core/command-generation/adapters/amazon-q.d.ts +17 -0
- package/dist/core/command-generation/adapters/amazon-q.js +32 -0
- package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
- package/dist/core/command-generation/adapters/antigravity.js +27 -0
- package/dist/core/command-generation/adapters/atomcode.d.ts +24 -0
- package/dist/core/command-generation/adapters/atomcode.js +46 -0
- package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
- package/dist/core/command-generation/adapters/auggie.js +28 -0
- package/dist/core/command-generation/adapters/bob.d.ts +18 -0
- package/dist/core/command-generation/adapters/bob.js +33 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +32 -0
- package/dist/core/command-generation/adapters/cline.d.ts +14 -0
- package/dist/core/command-generation/adapters/cline.js +27 -0
- package/dist/core/command-generation/adapters/codeassistant.d.ts +15 -0
- package/dist/core/command-generation/adapters/codeassistant.js +29 -0
- package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
- package/dist/core/command-generation/adapters/codebuddy.js +29 -0
- package/dist/core/command-generation/adapters/codestudio.d.ts +13 -0
- package/dist/core/command-generation/adapters/codestudio.js +27 -0
- package/dist/core/command-generation/adapters/command-code.d.ts +19 -0
- package/dist/core/command-generation/adapters/command-code.js +34 -0
- package/dist/core/command-generation/adapters/continue.d.ts +13 -0
- package/dist/core/command-generation/adapters/continue.js +33 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
- package/dist/core/command-generation/adapters/costrict.js +28 -0
- package/dist/core/command-generation/adapters/crush.d.ts +13 -0
- package/dist/core/command-generation/adapters/crush.js +30 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +31 -0
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/easycode.d.ts +17 -0
- package/dist/core/command-generation/adapters/easycode.js +28 -0
- package/dist/core/command-generation/adapters/factory.d.ts +13 -0
- package/dist/core/command-generation/adapters/factory.js +28 -0
- package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
- package/dist/core/command-generation/adapters/gemini.js +27 -0
- package/dist/core/command-generation/adapters/gigacode.d.ts +17 -0
- package/dist/core/command-generation/adapters/gigacode.js +31 -0
- package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
- package/dist/core/command-generation/adapters/github-copilot.js +27 -0
- package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
- package/dist/core/command-generation/adapters/iflow.js +30 -0
- package/dist/core/command-generation/adapters/index.d.ts +40 -0
- package/dist/core/command-generation/adapters/index.js +40 -0
- package/dist/core/command-generation/adapters/junie.d.ts +13 -0
- package/dist/core/command-generation/adapters/junie.js +27 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/kilocode.js +23 -0
- package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
- package/dist/core/command-generation/adapters/kiro.js +27 -0
- package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
- package/dist/core/command-generation/adapters/lingma.js +30 -0
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
- package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
- package/dist/core/command-generation/adapters/opencode.d.ts +14 -0
- package/dist/core/command-generation/adapters/opencode.js +38 -0
- package/dist/core/command-generation/adapters/pi.d.ts +18 -0
- package/dist/core/command-generation/adapters/pi.js +39 -0
- package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
- package/dist/core/command-generation/adapters/qoder.js +30 -0
- package/dist/core/command-generation/adapters/qwen.d.ts +17 -0
- package/dist/core/command-generation/adapters/qwen.js +31 -0
- package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/roocode.js +27 -0
- package/dist/core/command-generation/adapters/trae.d.ts +13 -0
- package/dist/core/command-generation/adapters/trae.js +28 -0
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +29 -0
- package/dist/core/command-generation/generator.js +43 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.d.ts +36 -0
- package/dist/core/command-generation/registry.js +114 -0
- package/dist/core/command-generation/toml.d.ts +19 -0
- package/dist/core/command-generation/toml.js +39 -0
- package/dist/core/command-generation/types.d.ts +63 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/command-generation/yaml.d.ts +36 -0
- package/dist/core/command-generation/yaml.js +48 -0
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +56 -0
- package/dist/core/config-schema.d.ts +98 -0
- package/dist/core/config-schema.js +308 -0
- package/dist/core/config.d.ts +50 -0
- package/dist/core/config.js +122 -0
- package/dist/core/file-state.d.ts +36 -0
- package/dist/core/file-state.js +144 -0
- package/dist/core/file-view.d.ts +13 -0
- package/dist/core/file-view.js +14 -0
- package/dist/core/global-config.d.ts +101 -0
- package/dist/core/global-config.js +182 -0
- package/dist/core/id.d.ts +17 -0
- package/dist/core/id.js +30 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +7 -0
- package/dist/core/init.d.ts +57 -0
- package/dist/core/init.js +836 -0
- package/dist/core/list.d.ts +11 -0
- package/dist/core/list.js +197 -0
- package/dist/core/migration.d.ts +121 -0
- package/dist/core/migration.js +540 -0
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/openspec-root.d.ts +47 -0
- package/dist/core/openspec-root.js +196 -0
- package/dist/core/planning-home.d.ts +16 -0
- package/dist/core/planning-home.js +67 -0
- package/dist/core/profile-sync-drift.d.ts +31 -0
- package/dist/core/profile-sync-drift.js +201 -0
- package/dist/core/profiles.d.ts +19 -0
- package/dist/core/profiles.js +33 -0
- package/dist/core/project-config.d.ts +132 -0
- package/dist/core/project-config.js +519 -0
- package/dist/core/references.d.ts +10 -0
- package/dist/core/references.js +46 -0
- package/dist/core/root-selection.d.ts +59 -0
- package/dist/core/root-selection.js +132 -0
- package/dist/core/shared/allowed-tools.d.ts +12 -0
- package/dist/core/shared/allowed-tools.js +12 -0
- package/dist/core/shared/ide-restart.d.ts +26 -0
- package/dist/core/shared/ide-restart.js +42 -0
- package/dist/core/shared/index.d.ts +11 -0
- package/dist/core/shared/index.js +11 -0
- package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
- package/dist/core/shared/skill-content-equivalence.js +45 -0
- package/dist/core/shared/skill-files.d.ts +7 -0
- package/dist/core/shared/skill-files.js +43 -0
- package/dist/core/shared/skill-generation.d.ts +49 -0
- package/dist/core/shared/skill-generation.js +125 -0
- package/dist/core/shared/skill-paths.d.ts +13 -0
- package/dist/core/shared/skill-paths.js +23 -0
- package/dist/core/shared/tool-detection.d.ts +99 -0
- package/dist/core/shared/tool-detection.js +318 -0
- package/dist/core/shared-skill-target.d.ts +51 -0
- package/dist/core/shared-skill-target.js +239 -0
- package/dist/core/store/errors.d.ts +20 -0
- package/dist/core/store/errors.js +22 -0
- package/dist/core/store/index.d.ts +2 -0
- package/dist/core/store/index.js +2 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/optional-workflow.d.ts +69 -0
- package/dist/core/templates/optional-workflow.js +140 -0
- package/dist/core/templates/types.d.ts +19 -0
- package/dist/core/templates/types.js +5 -0
- package/dist/core/templates/workflows/personal.d.ts +22 -0
- package/dist/core/templates/workflows/personal.js +225 -0
- package/dist/core/update-plan.d.ts +127 -0
- package/dist/core/update-plan.js +390 -0
- package/dist/core/update.d.ts +154 -0
- package/dist/core/update.js +723 -0
- package/dist/core/version-check.d.ts +188 -0
- package/dist/core/version-check.js +906 -0
- package/dist/core/zod-issues.d.ts +4 -0
- package/dist/core/zod-issues.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +35 -0
- package/dist/prompts/searchable-multi-select.js +188 -0
- package/dist/telemetry/config.d.ts +37 -0
- package/dist/telemetry/config.js +148 -0
- package/dist/telemetry/index.d.ts +39 -0
- package/dist/telemetry/index.js +217 -0
- package/dist/telemetry/opt-out.d.ts +17 -0
- package/dist/telemetry/opt-out.js +27 -0
- package/dist/ui/ascii-patterns.d.ts +16 -0
- package/dist/ui/ascii-patterns.js +133 -0
- package/dist/ui/welcome-screen.d.ts +18 -0
- package/dist/ui/welcome-screen.js +199 -0
- package/dist/utils/change-metadata.d.ts +116 -0
- package/dist/utils/change-metadata.js +324 -0
- package/dist/utils/change-utils.d.ts +79 -0
- package/dist/utils/change-utils.js +121 -0
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +16 -0
- package/dist/utils/command-references.d.ts +104 -0
- package/dist/utils/command-references.js +190 -0
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/executable-path.d.ts +5 -0
- package/dist/utils/executable-path.js +44 -0
- package/dist/utils/file-system.d.ts +58 -0
- package/dist/utils/file-system.js +432 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/interactive.d.ts +85 -0
- package/dist/utils/interactive.js +174 -0
- package/dist/utils/item-discovery.d.ts +15 -0
- package/dist/utils/item-discovery.js +33 -0
- package/dist/utils/line-endings.d.ts +34 -0
- package/dist/utils/line-endings.js +46 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/nested-change.d.ts +35 -0
- package/dist/utils/nested-change.js +185 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +94 -0
- package/dist/utils/task-progress.d.ts +67 -0
- package/dist/utils/task-progress.js +184 -0
- package/package.json +74 -4
- package/schemas//351/273/230/350/256/244/schema.yaml +68 -0
- package/schemas//351/273/230/350/256/244/templates//344/273/273/345/212/241/346/270/205/345/215/225.md +25 -0
- package/schemas//351/273/230/350/256/244/templates//346/265/213/350/257/225/347/224/250/344/276/213.md +34 -0
- package/schemas//351/273/230/350/256/244/templates//350/256/276/350/256/241/346/226/207/346/241/243.md +172 -0
- package/schemas//351/273/230/350/256/244/templates//351/234/200/346/261/202/346/226/207/346/241/243.md +19 -0
- package/skill-files//344/273/273/345/212/241/references//346/212/244/346/240/217.md +43 -0
- package/skill-files//344/273/273/345/212/241/references//347/262/222/345/272/246.md +23 -0
- package/skill-files//344/273/273/345/212/241/references//347/272/246/346/235/237.md +8 -0
- package/skill-files//344/273/273/345/212/241/scripts/check-tasks.mjs +83 -0
- package/skill-files//345/206/231Java/references/Java/350/247/204/350/214/203.md +25 -0
- package/skill-files//345/206/231Java/references//345/215/261/351/231/251/345/206/231/346/263/225.md +27 -0
- package/skill-files//345/206/231Java/references//345/256/214/346/210/220/345/257/271/347/205/247.md +15 -0
- package/skill-files//345/206/231Java/references//346/212/244/346/240/217.md +35 -0
- package/skill-files//345/206/231Java/references//347/272/246/346/235/237.md +8 -0
- package/skill-files//345/206/231Java/references//350/270/251/345/235/221.md +17 -0
- package/skill-files//345/256/241Java/references//346/212/244/346/240/217.md +19 -0
- package/skill-files//345/256/241Java/references//347/244/272/344/276/213.md +23 -0
- package/skill-files//345/256/241Java/references//347/272/246/346/235/237.md +7 -0
- package/skill-files//345/256/241Java/references//350/247/204/345/210/231.md +23 -0
- package/skill-files//345/256/241Java/references//351/242/206/345/237/237/346/243/200/351/241/271.md +58 -0
- package/skill-files//345/256/241/346/226/207/346/241/243/references//346/212/244/346/240/217.md +18 -0
- package/skill-files//345/256/241/346/226/207/346/241/243/references//347/272/246/346/235/237.md +8 -0
- package/skill-files//345/256/241/346/226/207/346/241/243/references//347/273/264/345/272/246.md +17 -0
- package/skill-files//345/256/241/346/226/207/346/241/243/references//350/270/251/345/235/221.md +11 -0
- package/skill-files//346/226/260/345/273/272/references//344/272/244/346/216/245.md +11 -0
- package/skill-files//346/226/260/345/273/272/references//346/212/244/346/240/217.md +14 -0
- package/skill-files//346/226/260/345/273/272/references//347/272/246/346/235/237.md +7 -0
- package/skill-files//346/267/261/346/200/235/config/prompts.yaml +114 -0
- package/skill-files//346/267/261/346/200/235/references//344/270/211/346/226/271/346/240/270/345/257/271.md +72 -0
- package/skill-files//346/267/261/346/200/235/references//344/276/213/345/255/220.md +39 -0
- package/skill-files//346/267/261/346/200/235/references//346/200/216/344/271/210/346/203/263.md +61 -0
- package/skill-files//346/267/261/346/200/235/references//346/212/244/346/240/217.md +26 -0
- package/skill-files//346/267/261/346/200/235/references//347/202/271/351/200/211/346/217/220/351/227/256.md +66 -0
- package/skill-files//346/267/261/346/200/235/references//347/231/273/350/256/260.md +45 -0
- package/skill-files//346/267/261/346/200/235/references//347/272/246/346/235/237.md +9 -0
- package/skill-files//347/224/250/344/276/213/references//346/200/216/344/271/210/345/206/231.md +13 -0
- package/skill-files//347/224/250/344/276/213/references//346/212/244/346/240/217.md +42 -0
- package/skill-files//347/224/250/344/276/213/references//347/272/246/346/235/237.md +5 -0
- package/skill-files//347/224/250/344/276/213/scripts/check-cases.mjs +70 -0
- package/skill-files//350/256/276/350/256/241/references//345/207/206/345/244/207.md +25 -0
- package/skill-files//350/256/276/350/256/241/references//346/212/244/346/240/217.md +134 -0
- package/skill-files//350/256/276/350/256/241/references//346/226/207/351/243/216.md +17 -0
- package/skill-files//350/256/276/350/256/241/references//347/225/214/351/235/242.md +18 -0
- package/skill-files//350/256/276/350/256/241/references//347/261/273/345/236/213.md +19 -0
- package/skill-files//350/256/276/350/256/241/references//347/272/246/346/235/237.md +25 -0
- package/skill-files//350/256/276/350/256/241/scripts/_smoke.mjs +72 -0
- package/skill-files//350/256/276/350/256/241/scripts/check-design.mjs +331 -0
- package/skill-files//351/234/200/346/261/202/references//346/212/244/346/240/217.md +26 -0
- package/skill-files//351/234/200/346/261/202/references//346/226/207/351/243/216.md +23 -0
- package/skill-files//351/234/200/346/261/202/references//346/267/261/346/200/235/346/221/204/345/205/245.md +21 -0
- package/skill-files//351/234/200/346/261/202/references//347/272/246/346/235/237.md +8 -0
- package/skill-files//351/234/200/346/261/202/references//351/234/200/346/261/202/345/276/205/345/256/232.md +13 -0
- package/skill-files//351/234/200/346/261/202/scripts/check-requirements.mjs +60 -0
- package/skill-files//351/252/214/346/224/266/references//346/212/244/346/240/217.md +27 -0
- package/skill-files//351/252/214/346/224/266/references//346/240/270/345/257/271.md +17 -0
- package/skill-files//351/252/214/346/224/266/references//347/272/246/346/235/237.md +7 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strings rendered into agent guidance. One line in, one line out: control
|
|
3
|
+
* characters and newlines must never let hostile content forge instruction lines.
|
|
4
|
+
*/
|
|
5
|
+
export function sanitizeInline(value, maxLength = 300) {
|
|
6
|
+
const flattened = value
|
|
7
|
+
.replace(/[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u206f]+/g, ' ')
|
|
8
|
+
.trim();
|
|
9
|
+
return flattened.length > maxLength ? `${flattened.slice(0, maxLength)}…` : flattened;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The tags the instruction printer uses to frame its blocks. A block ends at
|
|
13
|
+
* its own closing tag and nowhere else, so this is the entire breakout
|
|
14
|
+
* surface: neutralize these and repo-supplied text cannot escape the element
|
|
15
|
+
* that marks it as data.
|
|
16
|
+
*/
|
|
17
|
+
const ENVELOPE_TAGS = [
|
|
18
|
+
'artifact',
|
|
19
|
+
'dependencies',
|
|
20
|
+
'dependency',
|
|
21
|
+
'description',
|
|
22
|
+
'instruction',
|
|
23
|
+
'output',
|
|
24
|
+
'path',
|
|
25
|
+
'project_context',
|
|
26
|
+
'rules',
|
|
27
|
+
'success_criteria',
|
|
28
|
+
'task',
|
|
29
|
+
'template',
|
|
30
|
+
'unlocks',
|
|
31
|
+
'warning',
|
|
32
|
+
];
|
|
33
|
+
const ENVELOPE_TAG = new RegExp(`<(/?)(${ENVELOPE_TAGS.join('|')})(\\s[^<>]*)?>`, 'gi');
|
|
34
|
+
/** Neutralize the envelope's own tags in repo-supplied text. */
|
|
35
|
+
export function escapeEnvelopeTags(value) {
|
|
36
|
+
return value.replace(ENVELOPE_TAG, (_match, slash, tag, attrs) => `<${slash}${tag}${attrs ?? ''}>`);
|
|
37
|
+
}
|
|
38
|
+
/** Escape attribute metacharacters so a quote cannot close the attribute. */
|
|
39
|
+
export function escapeEnvelopeAttribute(value) {
|
|
40
|
+
return value
|
|
41
|
+
.replace(/&/g, '&')
|
|
42
|
+
.replace(/</g, '<')
|
|
43
|
+
.replace(/>/g, '>')
|
|
44
|
+
.replace(/"/g, '"');
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=references.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the local FreeSpec root for the supported commands.
|
|
3
|
+
*
|
|
4
|
+
* A root is the nearest ancestor whose `freespec/` directory is a planning
|
|
5
|
+
* directory (it contains a `<名称>变更` folder) or holds `config.yaml`.
|
|
6
|
+
* With neither, commands may treat the current directory as an implicit root.
|
|
7
|
+
*/
|
|
8
|
+
import { type PlanningHome } from './planning-home.js';
|
|
9
|
+
export type FreeSpecRootSource = 'nearest' | 'implicit';
|
|
10
|
+
export interface StoreSelectorOptions {
|
|
11
|
+
store?: string;
|
|
12
|
+
storePath?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ResolveFreeSpecRootOptions extends StoreSelectorOptions {
|
|
15
|
+
startPath?: string;
|
|
16
|
+
allowImplicitRoot?: boolean;
|
|
17
|
+
globalDataDir?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedFreeSpecRoot {
|
|
20
|
+
path: string;
|
|
21
|
+
changesDir: string;
|
|
22
|
+
specsDir: string;
|
|
23
|
+
defaultSchema: string;
|
|
24
|
+
source: FreeSpecRootSource;
|
|
25
|
+
}
|
|
26
|
+
export interface RootSelectionDiagnostic {
|
|
27
|
+
severity: 'error';
|
|
28
|
+
code: string;
|
|
29
|
+
message: string;
|
|
30
|
+
target?: string;
|
|
31
|
+
fix?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare class RootSelectionError extends Error {
|
|
34
|
+
readonly diagnostic: RootSelectionDiagnostic;
|
|
35
|
+
constructor(message: string, code: string, options?: {
|
|
36
|
+
target?: string;
|
|
37
|
+
fix?: string;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export declare function isRootSelectionError(error: unknown): error is RootSelectionError;
|
|
41
|
+
export declare function resolveFreeSpecRoot(options?: ResolveFreeSpecRootOptions): Promise<ResolvedFreeSpecRoot>;
|
|
42
|
+
export interface RootOutput {
|
|
43
|
+
path: string;
|
|
44
|
+
source: FreeSpecRootSource;
|
|
45
|
+
}
|
|
46
|
+
export declare function toRootOutput(root: ResolvedFreeSpecRoot): RootOutput;
|
|
47
|
+
export declare function toPlanningHome(root: ResolvedFreeSpecRoot): PlanningHome;
|
|
48
|
+
/**
|
|
49
|
+
* CLI adapter shared by the supported commands. In JSON mode a resolution
|
|
50
|
+
* failure is reported as a machine-readable payload on stdout with a
|
|
51
|
+
* non-zero exit code; the caller must return when this resolves to null.
|
|
52
|
+
*/
|
|
53
|
+
export declare function resolveRootForCommand(selector: StoreSelectorOptions, output?: {
|
|
54
|
+
json?: boolean;
|
|
55
|
+
failurePayload?: Record<string, unknown>;
|
|
56
|
+
/** Commands that require an existing root set this to false. */
|
|
57
|
+
allowImplicitRoot?: boolean;
|
|
58
|
+
}): Promise<ResolvedFreeSpecRoot | null>;
|
|
59
|
+
//# sourceMappingURL=root-selection.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the local FreeSpec root for the supported commands.
|
|
3
|
+
*
|
|
4
|
+
* A root is the nearest ancestor whose `freespec/` directory is a planning
|
|
5
|
+
* directory (it contains a `<名称>变更` folder) or holds `config.yaml`.
|
|
6
|
+
* With neither, commands may treat the current directory as an implicit root.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { changeParentDir, DEFAULT_SCHEMA } from './change-layout.js';
|
|
11
|
+
import { findRepoPlanningRootSync } from './planning-home.js';
|
|
12
|
+
import { classifyFreeSpecDir } from './project-config.js';
|
|
13
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
14
|
+
export class RootSelectionError extends Error {
|
|
15
|
+
diagnostic;
|
|
16
|
+
constructor(message, code, options = {}) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'RootSelectionError';
|
|
19
|
+
this.diagnostic = {
|
|
20
|
+
severity: 'error',
|
|
21
|
+
code,
|
|
22
|
+
message,
|
|
23
|
+
...options,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function isRootSelectionError(error) {
|
|
28
|
+
return error instanceof RootSelectionError;
|
|
29
|
+
}
|
|
30
|
+
function makeRoot(rootPath, source) {
|
|
31
|
+
return {
|
|
32
|
+
path: rootPath,
|
|
33
|
+
changesDir: changeParentDir(rootPath),
|
|
34
|
+
specsDir: path.join(rootPath, 'freespec', 'specs'),
|
|
35
|
+
defaultSchema: DEFAULT_SCHEMA,
|
|
36
|
+
source,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function canonicalDirectory(startPath) {
|
|
40
|
+
const resolved = path.resolve(startPath);
|
|
41
|
+
try {
|
|
42
|
+
const stats = fs.statSync(resolved);
|
|
43
|
+
const dir = stats.isDirectory() ? resolved : path.dirname(resolved);
|
|
44
|
+
return FileSystemUtils.canonicalizeExistingPath(dir);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return resolved;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* An `freespec/` directory is a root when it holds a change folder or a
|
|
52
|
+
* config file. A bare directory is not enough: `~/freespec` would otherwise
|
|
53
|
+
* capture every command under the home tree.
|
|
54
|
+
*/
|
|
55
|
+
function findQualifyingRootSync(startPath) {
|
|
56
|
+
let candidate = findRepoPlanningRootSync(startPath);
|
|
57
|
+
while (candidate) {
|
|
58
|
+
const { hasPlanningShape, pointer } = classifyFreeSpecDir(candidate);
|
|
59
|
+
if (hasPlanningShape || pointer.filePath) {
|
|
60
|
+
return candidate;
|
|
61
|
+
}
|
|
62
|
+
const parent = path.dirname(candidate);
|
|
63
|
+
if (parent === candidate) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
candidate = findRepoPlanningRootSync(parent);
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function rejectStoreSelection() {
|
|
71
|
+
throw new RootSelectionError('This FreeSpec build uses the local freespec/ directory. Store selection is not available.', 'store_not_supported', {
|
|
72
|
+
target: 'freespec.root',
|
|
73
|
+
fix: 'Run freespec init in the project, then rerun the command there.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export async function resolveFreeSpecRoot(options = {}) {
|
|
77
|
+
if (options.storePath !== undefined || options.store !== undefined) {
|
|
78
|
+
rejectStoreSelection();
|
|
79
|
+
}
|
|
80
|
+
const startPath = options.startPath ?? process.cwd();
|
|
81
|
+
const nearestRoot = findQualifyingRootSync(startPath);
|
|
82
|
+
if (nearestRoot) {
|
|
83
|
+
const { pointer } = classifyFreeSpecDir(nearestRoot);
|
|
84
|
+
if (pointer.value !== undefined && pointer.filePath) {
|
|
85
|
+
console.error(`Warning: ${pointer.filePath} declares store '${pointer.value}'. This build uses the local freespec/ directory and ignores that declaration.`);
|
|
86
|
+
}
|
|
87
|
+
return makeRoot(nearestRoot, 'nearest');
|
|
88
|
+
}
|
|
89
|
+
if (options.allowImplicitRoot === false) {
|
|
90
|
+
throw new RootSelectionError('No FreeSpec root found from the current directory.', 'no_freespec_root', { target: 'freespec.root', fix: 'Run freespec init to create a root here.' });
|
|
91
|
+
}
|
|
92
|
+
return makeRoot(canonicalDirectory(startPath), 'implicit');
|
|
93
|
+
}
|
|
94
|
+
export function toRootOutput(root) {
|
|
95
|
+
return {
|
|
96
|
+
path: root.path,
|
|
97
|
+
source: root.source,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function toPlanningHome(root) {
|
|
101
|
+
return {
|
|
102
|
+
kind: 'repo',
|
|
103
|
+
root: root.path,
|
|
104
|
+
changesDir: root.changesDir,
|
|
105
|
+
defaultSchema: root.defaultSchema,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* CLI adapter shared by the supported commands. In JSON mode a resolution
|
|
110
|
+
* failure is reported as a machine-readable payload on stdout with a
|
|
111
|
+
* non-zero exit code; the caller must return when this resolves to null.
|
|
112
|
+
*/
|
|
113
|
+
export async function resolveRootForCommand(selector, output = {}) {
|
|
114
|
+
try {
|
|
115
|
+
return await resolveFreeSpecRoot({
|
|
116
|
+
...(selector.store !== undefined ? { store: selector.store } : {}),
|
|
117
|
+
...(selector.storePath !== undefined ? { storePath: selector.storePath } : {}),
|
|
118
|
+
...(output.allowImplicitRoot !== undefined
|
|
119
|
+
? { allowImplicitRoot: output.allowImplicitRoot }
|
|
120
|
+
: {}),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (output.json && isRootSelectionError(error)) {
|
|
125
|
+
console.log(JSON.stringify({ ...(output.failurePayload ?? {}), status: [error.diagnostic] }, null, 2));
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=root-selection.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-approved tools for generated skills and slash commands, emitted as the
|
|
3
|
+
* `allowed-tools` frontmatter field (Agent Skills standard for SKILL.md;
|
|
4
|
+
* same field for Claude Code slash commands). Scoped to the FreeSpec CLI so
|
|
5
|
+
* agents that honor it stop prompting on each `freespec` call; the field
|
|
6
|
+
* only pre-approves — it does not restrict — so any other tool a skill or
|
|
7
|
+
* command needs (Read, Write, arbitrary Bash for builds/tests) stays
|
|
8
|
+
* available under the user's normal permission settings. Tools that don't
|
|
9
|
+
* recognize the field ignore it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const OPENSPEC_CLI_ALLOWED_TOOLS = "Bash(freespec:*)";
|
|
12
|
+
//# sourceMappingURL=allowed-tools.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-approved tools for generated skills and slash commands, emitted as the
|
|
3
|
+
* `allowed-tools` frontmatter field (Agent Skills standard for SKILL.md;
|
|
4
|
+
* same field for Claude Code slash commands). Scoped to the FreeSpec CLI so
|
|
5
|
+
* agents that honor it stop prompting on each `freespec` call; the field
|
|
6
|
+
* only pre-approves — it does not restrict — so any other tool a skill or
|
|
7
|
+
* command needs (Read, Write, arbitrary Bash for builds/tests) stays
|
|
8
|
+
* available under the user's normal permission settings. Tools that don't
|
|
9
|
+
* recognize the field ignore it.
|
|
10
|
+
*/
|
|
11
|
+
export const OPENSPEC_CLI_ALLOWED_TOOLS = 'Bash(freespec:*)';
|
|
12
|
+
//# sourceMappingURL=allowed-tools.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE restart hint
|
|
3
|
+
*
|
|
4
|
+
* Shared restart guidance for tools successfully configured by init or update.
|
|
5
|
+
* The wording covers additions, updates, and removals, including an empty
|
|
6
|
+
* workflow selection that removes every generated file.
|
|
7
|
+
*/
|
|
8
|
+
import type { Delivery } from '../global-config.js';
|
|
9
|
+
/** The surface a restart hint names. Absent when no hint is due. */
|
|
10
|
+
export type IdeRestartSurface = 'commands' | 'skills';
|
|
11
|
+
/**
|
|
12
|
+
* Both conditions stay coupled to the SAME tool: its surfaces are loaded by a
|
|
13
|
+
* long-running editor process (a CLI picks them up immediately, so a restart
|
|
14
|
+
* line would be wrong for it — see #1067), and it supports a generated surface
|
|
15
|
+
* under the active delivery. A CLI tool's commands must not determine the hint
|
|
16
|
+
* for an IDE tool that only supports skills. Commands take precedence when
|
|
17
|
+
* both surfaces are supported under the active delivery.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveIdeRestartSurface(toolIds: readonly string[], delivery: Delivery): IdeRestartSurface | null;
|
|
20
|
+
/**
|
|
21
|
+
* The restart line to print, or null when no restart is needed. Deliberately
|
|
22
|
+
* not "slash commands": Amazon Q's generated files are prompt-library entries
|
|
23
|
+
* invoked with `@`, so promising slash commands would be wrong for it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatIdeRestart(toolIds: readonly string[], delivery: Delivery): string | null;
|
|
26
|
+
//# sourceMappingURL=ide-restart.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE restart hint
|
|
3
|
+
*
|
|
4
|
+
* Shared restart guidance for tools successfully configured by init or update.
|
|
5
|
+
* The wording covers additions, updates, and removals, including an empty
|
|
6
|
+
* workflow selection that removes every generated file.
|
|
7
|
+
*/
|
|
8
|
+
import { AI_TOOLS } from '../config.js';
|
|
9
|
+
import { shouldGenerateCommandsForTool, shouldGenerateSkillsForTool, } from '../command-surface.js';
|
|
10
|
+
function isIdeResident(toolId) {
|
|
11
|
+
return Boolean(AI_TOOLS.find((tool) => tool.value === toolId)?.requiresIdeRestart);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Both conditions stay coupled to the SAME tool: its surfaces are loaded by a
|
|
15
|
+
* long-running editor process (a CLI picks them up immediately, so a restart
|
|
16
|
+
* line would be wrong for it — see #1067), and it supports a generated surface
|
|
17
|
+
* under the active delivery. A CLI tool's commands must not determine the hint
|
|
18
|
+
* for an IDE tool that only supports skills. Commands take precedence when
|
|
19
|
+
* both surfaces are supported under the active delivery.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveIdeRestartSurface(toolIds, delivery) {
|
|
22
|
+
const ideTools = [...new Set(toolIds)].filter(isIdeResident);
|
|
23
|
+
if (ideTools.some((toolId) => shouldGenerateCommandsForTool(toolId, delivery))) {
|
|
24
|
+
return 'commands';
|
|
25
|
+
}
|
|
26
|
+
if (ideTools.some((toolId) => shouldGenerateSkillsForTool(toolId, delivery))) {
|
|
27
|
+
return 'skills';
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The restart line to print, or null when no restart is needed. Deliberately
|
|
33
|
+
* not "slash commands": Amazon Q's generated files are prompt-library entries
|
|
34
|
+
* invoked with `@`, so promising slash commands would be wrong for it.
|
|
35
|
+
*/
|
|
36
|
+
export function formatIdeRestart(toolIds, delivery) {
|
|
37
|
+
const surface = resolveIdeRestartSurface(toolIds, delivery);
|
|
38
|
+
return surface
|
|
39
|
+
? `Restart your IDE to refresh ${surface}.`
|
|
40
|
+
: null;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ide-restart.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common code shared between init and update commands.
|
|
5
|
+
*/
|
|
6
|
+
export { SKILL_NAMES, type SkillName, COMMAND_IDS, type CommandId, type ToolSkillStatus, type ToolVersionStatus, getToolsWithSkillsDir, getToolSkillStatus, getToolStates, extractGeneratedByVersion, getToolVersionStatus, getConfiguredTools, getAllToolVersionStatus, } from './tool-detection.js';
|
|
7
|
+
export { type SkillTemplateEntry, type CommandTemplateEntry, getSkillTemplates, getCommandTemplates, getCommandContents, generateSkillContent, } from './skill-generation.js';
|
|
8
|
+
export { copySkillExtras, getSkillFilesRoot } from './skill-files.js';
|
|
9
|
+
export { type SkillCapableTool, toolSupportsSkills, getSkillCapableTools, hasGlobalSkillTarget, resolveToolSkillsDir, } from './skill-paths.js';
|
|
10
|
+
export { type IdeRestartSurface, resolveIdeRestartSurface, formatIdeRestart, } from './ide-restart.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Common code shared between init and update commands.
|
|
5
|
+
*/
|
|
6
|
+
export { SKILL_NAMES, COMMAND_IDS, getToolsWithSkillsDir, getToolSkillStatus, getToolStates, extractGeneratedByVersion, getToolVersionStatus, getConfiguredTools, getAllToolVersionStatus, } from './tool-detection.js';
|
|
7
|
+
export { getSkillTemplates, getCommandTemplates, getCommandContents, generateSkillContent, } from './skill-generation.js';
|
|
8
|
+
export { copySkillExtras, getSkillFilesRoot } from './skill-files.js';
|
|
9
|
+
export { toolSupportsSkills, getSkillCapableTools, hasGlobalSkillTarget, resolveToolSkillsDir, } from './skill-paths.js';
|
|
10
|
+
export { resolveIdeRestartSurface, formatIdeRestart, } from './ide-restart.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns whether a legacy Codex skill differs from the current canonical
|
|
3
|
+
* replacement only by generated version, checkout line endings/BOM, or the
|
|
4
|
+
* known Codex/generic dual-reference expansion.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isLegacyCodexSkillEquivalentToCurrent(legacyContent: string, currentContent: string): boolean;
|
|
7
|
+
//# sourceMappingURL=skill-content-equivalence.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OPENSPEC_SKILL_NAMES } from '../config.js';
|
|
2
|
+
const GENERATED_VERSION = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
3
|
+
const OPENSPEC_SKILL_NAME_SET = new Set(OPENSPEC_SKILL_NAMES);
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes checkout line endings and a valid generated version inside the
|
|
6
|
+
* YAML frontmatter. Free-form `generatedBy` text in the instructions remains
|
|
7
|
+
* material.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeGeneratedSkill(content) {
|
|
10
|
+
const normalized = content.replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
|
|
11
|
+
const frontmatter = normalized.match(/^---\n[\s\S]*?\n---(?:\n|$)/)?.[0];
|
|
12
|
+
if (!frontmatter)
|
|
13
|
+
return normalized;
|
|
14
|
+
// `[ \t]` rather than `\s` so a leading/trailing whitespace run can never
|
|
15
|
+
// cross a newline: an `m`-anchored `\s*` re-scans from every line start,
|
|
16
|
+
// which is quadratic on a whitespace-heavy frontmatter. YAML indentation is
|
|
17
|
+
// spaces and tabs only, so matching is unchanged.
|
|
18
|
+
const versionLine = /^([ \t]*generatedBy:[ \t]*)(?:"([^"\n]+)"|'([^'\n]+)'|([^\s"'#]+))[ \t]*$/m;
|
|
19
|
+
const normalizedFrontmatter = frontmatter.replace(versionLine, (line, prefix, doubleQuoted, singleQuoted, bare) => {
|
|
20
|
+
const version = doubleQuoted ?? singleQuoted ?? bare;
|
|
21
|
+
return version && GENERATED_VERSION.test(version)
|
|
22
|
+
? `${prefix}"<generated-version>"`
|
|
23
|
+
: line;
|
|
24
|
+
});
|
|
25
|
+
return normalizedFrontmatter + normalized.slice(frontmatter.length);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Converts only known generated dual references in current Codex content back
|
|
29
|
+
* to the direct syntax used by legacy `.codex` output.
|
|
30
|
+
*/
|
|
31
|
+
function toLegacyCodexReferences(content) {
|
|
32
|
+
return content.replace(/\$(freespec-[a-z0-9-]+) \(Codex\) or \/\1 \(other agents\)/g, (match, skillName) => OPENSPEC_SKILL_NAME_SET.has(skillName) ? `$${skillName}` : match);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns whether a legacy Codex skill differs from the current canonical
|
|
36
|
+
* replacement only by generated version, checkout line endings/BOM, or the
|
|
37
|
+
* known Codex/generic dual-reference expansion.
|
|
38
|
+
*/
|
|
39
|
+
export function isLegacyCodexSkillEquivalentToCurrent(legacyContent, currentContent) {
|
|
40
|
+
const normalizedLegacy = normalizeGeneratedSkill(legacyContent);
|
|
41
|
+
const normalizedCurrent = normalizeGeneratedSkill(currentContent);
|
|
42
|
+
return (normalizedLegacy === normalizedCurrent ||
|
|
43
|
+
normalizedLegacy === toLegacyCodexReferences(normalizedCurrent));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=skill-content-equivalence.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-root folder of skill extras. From dist/core/shared this is
|
|
3
|
+
* <package>/skill-files. Init and update copy it next to each SKILL.md.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSkillFilesRoot(): string;
|
|
6
|
+
export declare function copySkillExtras(skillDir: string, workflowId: string): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=skill-files.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const EXTRA_DIRS = ['references', 'config', 'scripts'];
|
|
5
|
+
/**
|
|
6
|
+
* Package-root folder of skill extras. From dist/core/shared this is
|
|
7
|
+
* <package>/skill-files. Init and update copy it next to each SKILL.md.
|
|
8
|
+
*/
|
|
9
|
+
export function getSkillFilesRoot() {
|
|
10
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
11
|
+
return path.join(path.dirname(currentFile), '..', '..', '..', 'skill-files');
|
|
12
|
+
}
|
|
13
|
+
async function copyTree(source, target) {
|
|
14
|
+
await fs.promises.mkdir(target, { recursive: true });
|
|
15
|
+
const entries = await fs.promises.readdir(source, { withFileTypes: true });
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const from = path.join(source, entry.name);
|
|
18
|
+
const to = path.join(target, entry.name);
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
await copyTree(from, to);
|
|
21
|
+
}
|
|
22
|
+
else if (entry.isFile()) {
|
|
23
|
+
await fs.promises.copyFile(from, to);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function copySkillExtras(skillDir, workflowId) {
|
|
28
|
+
if (!/^[\u4e00-\u9fffA-Za-z0-9]+$/u.test(workflowId)) {
|
|
29
|
+
throw new Error(`Refusing to copy skill files for ${workflowId}`);
|
|
30
|
+
}
|
|
31
|
+
const sourceRoot = path.join(getSkillFilesRoot(), workflowId);
|
|
32
|
+
if (!fs.existsSync(sourceRoot))
|
|
33
|
+
return;
|
|
34
|
+
for (const name of EXTRA_DIRS) {
|
|
35
|
+
const source = path.join(sourceRoot, name);
|
|
36
|
+
if (!fs.existsSync(source))
|
|
37
|
+
continue;
|
|
38
|
+
const target = path.join(skillDir, name);
|
|
39
|
+
await fs.promises.rm(target, { recursive: true, force: true });
|
|
40
|
+
await copyTree(source, target);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=skill-files.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Generation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for generating skill and command files.
|
|
5
|
+
*/
|
|
6
|
+
import type { SkillTemplate, CommandTemplate } from '../templates/types.js';
|
|
7
|
+
import type { CommandContent } from '../command-generation/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Skill template with directory name and workflow ID mapping.
|
|
10
|
+
*/
|
|
11
|
+
export interface SkillTemplateEntry {
|
|
12
|
+
template: SkillTemplate;
|
|
13
|
+
dirName: string;
|
|
14
|
+
workflowId: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Command template with ID mapping.
|
|
18
|
+
*/
|
|
19
|
+
export interface CommandTemplateEntry {
|
|
20
|
+
template: CommandTemplate;
|
|
21
|
+
id: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Gets skill templates with their directory names, optionally filtered by workflow IDs.
|
|
25
|
+
*
|
|
26
|
+
* @param workflowFilter - If provided, only return templates whose workflowId is in this array
|
|
27
|
+
*/
|
|
28
|
+
export declare function getSkillTemplates(workflowFilter?: readonly string[]): SkillTemplateEntry[];
|
|
29
|
+
/**
|
|
30
|
+
* Gets command templates with their IDs, optionally filtered by workflow IDs.
|
|
31
|
+
*
|
|
32
|
+
* @param workflowFilter - If provided, only return templates whose id is in this array
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCommandTemplates(workflowFilter?: readonly string[]): CommandTemplateEntry[];
|
|
35
|
+
/**
|
|
36
|
+
* Converts command templates to CommandContent array, optionally filtered by workflow IDs.
|
|
37
|
+
*
|
|
38
|
+
* @param workflowFilter - If provided, only return contents whose id is in this array
|
|
39
|
+
*/
|
|
40
|
+
export declare function getCommandContents(workflowFilter?: readonly string[]): CommandContent[];
|
|
41
|
+
/**
|
|
42
|
+
* Generates skill file content with YAML frontmatter.
|
|
43
|
+
*
|
|
44
|
+
* @param template - The skill template
|
|
45
|
+
* @param generatedByVersion - The FreeSpec version to embed in the file
|
|
46
|
+
* @param transformInstructions - Optional callback to transform the instructions content
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateSkillContent(template: SkillTemplate, generatedByVersion: string, transformInstructions?: (instructions: string) => string): string;
|
|
49
|
+
//# sourceMappingURL=skill-generation.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Generation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for generating skill and command files.
|
|
5
|
+
*/
|
|
6
|
+
import { getAskCommandTemplate, getAskSkillTemplate, getCasesCommandTemplate, getCasesSkillTemplate, getCheckCommandTemplate, getCheckSkillTemplate, getDesignCommandTemplate, getDesignSkillTemplate, getJavaImplCommandTemplate, getJavaImplSkillTemplate, getJavaReviewCommandTemplate, getJavaReviewSkillTemplate, getNewCommandTemplate, getNewSkillTemplate, getRequirementsCommandTemplate, getRequirementsSkillTemplate, getReviewDocsCommandTemplate, getReviewDocsSkillTemplate, getTasksCommandTemplate, getTasksSkillTemplate, } from '../templates/workflows/personal.js';
|
|
7
|
+
import { assertWorkflowConditionalsResolved, resolveOptionalWorkflows, } from '../templates/optional-workflow.js';
|
|
8
|
+
import { ALL_WORKFLOWS } from '../profiles.js';
|
|
9
|
+
import { OPENSPEC_CLI_ALLOWED_TOOLS } from './allowed-tools.js';
|
|
10
|
+
/**
|
|
11
|
+
* The workflow set a template body is rendered against.
|
|
12
|
+
*
|
|
13
|
+
* `workflowFilter` is both the list of workflows to install and the set a
|
|
14
|
+
* template may refer to, so resolving optional-workflow conditionals here —
|
|
15
|
+
* the one place every generation path (init, update, migration, the skills.sh
|
|
16
|
+
* distribution) already funnels through — keeps a reference to an uninstalled
|
|
17
|
+
* workflow out of every generated file (#1734, umbrella #919).
|
|
18
|
+
*
|
|
19
|
+
* With no filter, every workflow is installed (that is what an unfiltered call
|
|
20
|
+
* means), so the installed branch is kept.
|
|
21
|
+
*/
|
|
22
|
+
function resolveInstalledWorkflows(workflowFilter) {
|
|
23
|
+
return new Set(workflowFilter ?? ALL_WORKFLOWS);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Gets skill templates with their directory names, optionally filtered by workflow IDs.
|
|
27
|
+
*
|
|
28
|
+
* @param workflowFilter - If provided, only return templates whose workflowId is in this array
|
|
29
|
+
*/
|
|
30
|
+
export function getSkillTemplates(workflowFilter) {
|
|
31
|
+
const all = [
|
|
32
|
+
{ template: getNewSkillTemplate(), dirName: 'freespec-新建', workflowId: '新建' },
|
|
33
|
+
{ template: getAskSkillTemplate(), dirName: 'freespec-深思', workflowId: '深思' },
|
|
34
|
+
{ template: getRequirementsSkillTemplate(), dirName: 'freespec-需求', workflowId: '需求' },
|
|
35
|
+
{ template: getDesignSkillTemplate(), dirName: 'freespec-设计', workflowId: '设计' },
|
|
36
|
+
{ template: getTasksSkillTemplate(), dirName: 'freespec-任务', workflowId: '任务' },
|
|
37
|
+
{ template: getReviewDocsSkillTemplate(), dirName: 'freespec-审文档', workflowId: '审文档' },
|
|
38
|
+
{ template: getCasesSkillTemplate(), dirName: 'freespec-用例', workflowId: '用例' },
|
|
39
|
+
{ template: getCheckSkillTemplate(), dirName: 'freespec-验收', workflowId: '验收' },
|
|
40
|
+
{ template: getJavaImplSkillTemplate(), dirName: 'freespec-写Java', workflowId: '写Java' },
|
|
41
|
+
{ template: getJavaReviewSkillTemplate(), dirName: 'freespec-审Java', workflowId: '审Java' },
|
|
42
|
+
];
|
|
43
|
+
const installed = resolveInstalledWorkflows(workflowFilter);
|
|
44
|
+
const selected = workflowFilter ? all.filter(entry => installed.has(entry.workflowId)) : all;
|
|
45
|
+
return selected.map(entry => ({
|
|
46
|
+
...entry,
|
|
47
|
+
template: {
|
|
48
|
+
...entry.template,
|
|
49
|
+
instructions: resolveOptionalWorkflows(entry.template.instructions, installed),
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets command templates with their IDs, optionally filtered by workflow IDs.
|
|
55
|
+
*
|
|
56
|
+
* @param workflowFilter - If provided, only return templates whose id is in this array
|
|
57
|
+
*/
|
|
58
|
+
export function getCommandTemplates(workflowFilter) {
|
|
59
|
+
const all = [
|
|
60
|
+
{ template: getNewCommandTemplate(), id: '新建' },
|
|
61
|
+
{ template: getAskCommandTemplate(), id: '深思' },
|
|
62
|
+
{ template: getRequirementsCommandTemplate(), id: '需求' },
|
|
63
|
+
{ template: getDesignCommandTemplate(), id: '设计' },
|
|
64
|
+
{ template: getTasksCommandTemplate(), id: '任务' },
|
|
65
|
+
{ template: getReviewDocsCommandTemplate(), id: '审文档' },
|
|
66
|
+
{ template: getCasesCommandTemplate(), id: '用例' },
|
|
67
|
+
{ template: getCheckCommandTemplate(), id: '验收' },
|
|
68
|
+
{ template: getJavaImplCommandTemplate(), id: '写Java' },
|
|
69
|
+
{ template: getJavaReviewCommandTemplate(), id: '审Java' },
|
|
70
|
+
];
|
|
71
|
+
const installed = resolveInstalledWorkflows(workflowFilter);
|
|
72
|
+
const selected = workflowFilter ? all.filter(entry => installed.has(entry.id)) : all;
|
|
73
|
+
return selected.map(entry => ({
|
|
74
|
+
...entry,
|
|
75
|
+
template: {
|
|
76
|
+
...entry.template,
|
|
77
|
+
content: resolveOptionalWorkflows(entry.template.content, installed),
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Converts command templates to CommandContent array, optionally filtered by workflow IDs.
|
|
83
|
+
*
|
|
84
|
+
* @param workflowFilter - If provided, only return contents whose id is in this array
|
|
85
|
+
*/
|
|
86
|
+
export function getCommandContents(workflowFilter) {
|
|
87
|
+
const commandTemplates = getCommandTemplates(workflowFilter);
|
|
88
|
+
return commandTemplates.map(({ template, id }) => ({
|
|
89
|
+
id,
|
|
90
|
+
name: template.name,
|
|
91
|
+
description: template.description,
|
|
92
|
+
category: template.category,
|
|
93
|
+
tags: template.tags,
|
|
94
|
+
body: template.content,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generates skill file content with YAML frontmatter.
|
|
99
|
+
*
|
|
100
|
+
* @param template - The skill template
|
|
101
|
+
* @param generatedByVersion - The FreeSpec version to embed in the file
|
|
102
|
+
* @param transformInstructions - Optional callback to transform the instructions content
|
|
103
|
+
*/
|
|
104
|
+
export function generateSkillContent(template, generatedByVersion, transformInstructions) {
|
|
105
|
+
const instructions = transformInstructions
|
|
106
|
+
? transformInstructions(template.instructions)
|
|
107
|
+
: template.instructions;
|
|
108
|
+
assertWorkflowConditionalsResolved(instructions, `Skill '${template.name}' was generated without resolving its optional-workflow blocks`);
|
|
109
|
+
return `---
|
|
110
|
+
name: ${template.name}
|
|
111
|
+
description: ${template.description}
|
|
112
|
+
disable-model-invocation: true
|
|
113
|
+
allowed-tools: ${OPENSPEC_CLI_ALLOWED_TOOLS}
|
|
114
|
+
license: ${template.license || 'MIT'}
|
|
115
|
+
compatibility: ${template.compatibility || 'Requires freespec CLI.'}
|
|
116
|
+
metadata:
|
|
117
|
+
author: ${template.metadata?.author || 'freespec'}
|
|
118
|
+
version: "${template.metadata?.version || '1.0'}"
|
|
119
|
+
generatedBy: "${generatedByVersion}"
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
${instructions}
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=skill-generation.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AIToolOption } from '../config.js';
|
|
2
|
+
export type SkillCapableTool = AIToolOption & ({
|
|
3
|
+
skillsDir: string;
|
|
4
|
+
} | {
|
|
5
|
+
globalSkillsDir: string;
|
|
6
|
+
});
|
|
7
|
+
export declare function toolSupportsSkills(tool: AIToolOption): tool is SkillCapableTool;
|
|
8
|
+
export declare function getSkillCapableTools(): SkillCapableTool[];
|
|
9
|
+
export declare function hasGlobalSkillTarget(tool: AIToolOption): boolean;
|
|
10
|
+
export declare function resolveToolSkillsDir(projectRoot: string, tool: SkillCapableTool, options?: {
|
|
11
|
+
homeDir?: string;
|
|
12
|
+
}): string;
|
|
13
|
+
//# sourceMappingURL=skill-paths.d.ts.map
|