@thinkingsage/kanon 0.8.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/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/bridge/mcp-server.cjs +14171 -0
- package/package.json +98 -0
- package/src/adapters/capabilities.ts +178 -0
- package/src/adapters/claude-code.ts +110 -0
- package/src/adapters/cline.ts +98 -0
- package/src/adapters/codex.ts +173 -0
- package/src/adapters/copilot.ts +106 -0
- package/src/adapters/cursor.ts +97 -0
- package/src/adapters/degradation.ts +95 -0
- package/src/adapters/index.ts +324 -0
- package/src/adapters/kiro-frontmatter.ts +139 -0
- package/src/adapters/kiro-inclusion.ts +86 -0
- package/src/adapters/kiro.ts +412 -0
- package/src/adapters/qdeveloper.ts +115 -0
- package/src/adapters/types.ts +81 -0
- package/src/adapters/windsurf.ts +96 -0
- package/src/admin.ts +283 -0
- package/src/asset-conventions.ts +118 -0
- package/src/attribution-backfill.ts +319 -0
- package/src/attribution-report.ts +95 -0
- package/src/attribution.ts +239 -0
- package/src/backends/github.ts +194 -0
- package/src/backends/http.ts +122 -0
- package/src/backends/index.ts +39 -0
- package/src/backends/local.ts +47 -0
- package/src/backends/s3.ts +157 -0
- package/src/backends/types.ts +59 -0
- package/src/base-cache.ts +270 -0
- package/src/browse-ui.ts +3754 -0
- package/src/browse.ts +1038 -0
- package/src/build.ts +1108 -0
- package/src/catalog.ts +204 -0
- package/src/cli-deprecated.ts +29 -0
- package/src/cli.ts +773 -0
- package/src/collection-admin.ts +287 -0
- package/src/collection-builder.ts +464 -0
- package/src/collections.ts +116 -0
- package/src/compatibility.ts +105 -0
- package/src/config.ts +743 -0
- package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
- package/src/eval.ts +1169 -0
- package/src/file-writer.ts +61 -0
- package/src/format-registry.ts +141 -0
- package/src/guild/auto-updater.ts +163 -0
- package/src/guild/backend-resolver.ts +49 -0
- package/src/guild/cli.ts +592 -0
- package/src/guild/collection-expander.ts +47 -0
- package/src/guild/global-cache.ts +247 -0
- package/src/guild/hook-generator.ts +100 -0
- package/src/guild/manifest.ts +154 -0
- package/src/guild/path-utils.ts +12 -0
- package/src/guild/sync.ts +622 -0
- package/src/guild/version-resolver.ts +42 -0
- package/src/help/metadata.ts +445 -0
- package/src/help/renderer.ts +265 -0
- package/src/help/typo-suggester.ts +25 -0
- package/src/hooks/expression.ts +493 -0
- package/src/hooks/pipeline.ts +141 -0
- package/src/import.ts +773 -0
- package/src/importers/claude-code.ts +134 -0
- package/src/importers/cline.ts +103 -0
- package/src/importers/codex.ts +140 -0
- package/src/importers/copilot.ts +103 -0
- package/src/importers/cursor.ts +105 -0
- package/src/importers/index.ts +390 -0
- package/src/importers/kiro.ts +110 -0
- package/src/importers/qdeveloper.ts +103 -0
- package/src/importers/types.ts +54 -0
- package/src/importers/windsurf.ts +104 -0
- package/src/install.ts +1005 -0
- package/src/manifest-admin.ts +306 -0
- package/src/mcp-bridge.ts +240 -0
- package/src/mutation/delta.ts +50 -0
- package/src/mutation/history.ts +66 -0
- package/src/mutation/operators.ts +524 -0
- package/src/mutation/runner.ts +332 -0
- package/src/new.ts +106 -0
- package/src/outcomes/collision.ts +127 -0
- package/src/outcomes/normalize.ts +208 -0
- package/src/outcomes/registry.ts +173 -0
- package/src/parser.ts +446 -0
- package/src/provenance-backfill-cli.ts +319 -0
- package/src/provenance-backfill.ts +520 -0
- package/src/publish.ts +354 -0
- package/src/reconcile-orchestrator.ts +502 -0
- package/src/reconcile-report-renderer.ts +176 -0
- package/src/resolve-body.ts +15 -0
- package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
- package/src/rosetta/builtins/contracts.ts +1033 -0
- package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
- package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
- package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
- package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
- package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
- package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
- package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
- package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
- package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
- package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
- package/src/rosetta/builtins/sources/cline-native.ts +176 -0
- package/src/rosetta/builtins/sources/codex-native.ts +343 -0
- package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
- package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
- package/src/rosetta/builtins/sources/index.ts +95 -0
- package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
- package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
- package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
- package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
- package/src/rosetta/builtins/sources/superpowers.ts +240 -0
- package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
- package/src/rosetta/builtins/targets/claude-code.ts +181 -0
- package/src/rosetta/builtins/targets/cline.ts +87 -0
- package/src/rosetta/builtins/targets/codex.ts +226 -0
- package/src/rosetta/builtins/targets/copilot.ts +103 -0
- package/src/rosetta/builtins/targets/cursor.ts +87 -0
- package/src/rosetta/builtins/targets/index.ts +60 -0
- package/src/rosetta/builtins/targets/kiro.ts +278 -0
- package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
- package/src/rosetta/builtins/targets/windsurf.ts +87 -0
- package/src/rosetta/canonical.ts +729 -0
- package/src/rosetta/compatibility.ts +432 -0
- package/src/rosetta/contracts.ts +329 -0
- package/src/rosetta/detector.ts +724 -0
- package/src/rosetta/diagnostics.ts +630 -0
- package/src/rosetta/engine-bootstrap.ts +103 -0
- package/src/rosetta/engine.ts +744 -0
- package/src/rosetta/index.ts +381 -0
- package/src/rosetta/inspection.ts +530 -0
- package/src/rosetta/plan.ts +448 -0
- package/src/rosetta/provenance-digest.ts +369 -0
- package/src/rosetta/reconcile.ts +812 -0
- package/src/rosetta/redaction.ts +467 -0
- package/src/rosetta/registry.ts +712 -0
- package/src/rosetta/renderers.ts +571 -0
- package/src/rosetta/request-guard.ts +335 -0
- package/src/rosetta/resolution.ts +419 -0
- package/src/rosetta/source-accounting.ts +233 -0
- package/src/rosetta/templates.ts +129 -0
- package/src/rosetta-cli.ts +717 -0
- package/src/rosetta-docs-generator.ts +793 -0
- package/src/rosetta-profiles-cli.ts +367 -0
- package/src/schemas.ts +1712 -0
- package/src/spec-coordination.ts +1141 -0
- package/src/temper.ts +747 -0
- package/src/template-bundle-loader.ts +312 -0
- package/src/template-engine.ts +53 -0
- package/src/translation-application-policy.ts +496 -0
- package/src/translation-orchestrator.ts +1013 -0
- package/src/translation-plan-applier.ts +473 -0
- package/src/tutorial.ts +305 -0
- package/src/validate.ts +1093 -0
- package/src/versioning.ts +553 -0
- package/src/wizard.ts +660 -0
- package/src/workspace.ts +237 -0
- package/templates/eval-contexts/claude-code.md.njk +6 -0
- package/templates/eval-contexts/cline.md.njk +6 -0
- package/templates/eval-contexts/copilot.md.njk +6 -0
- package/templates/eval-contexts/cursor.md.njk +6 -0
- package/templates/eval-contexts/kiro.md.njk +10 -0
- package/templates/eval-contexts/qdeveloper.md.njk +6 -0
- package/templates/eval-contexts/windsurf.md.njk +6 -0
- package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
- package/templates/harness-adapters/_base/base.md.njk +16 -0
- package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
- package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
- package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
- package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
- package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
- package/templates/harness-adapters/cline/hook.sh.njk +4 -0
- package/templates/harness-adapters/cline/mcp.json.njk +1 -0
- package/templates/harness-adapters/cline/rule.md.njk +1 -0
- package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
- package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
- package/templates/harness-adapters/codex/skill.md.njk +27 -0
- package/templates/harness-adapters/copilot/agents.md.njk +1 -0
- package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
- package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
- package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
- package/templates/harness-adapters/cursor/rule.md.njk +6 -0
- package/templates/harness-adapters/kiro/hook.json.njk +1 -0
- package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
- package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
- package/templates/harness-adapters/kiro/power.md.njk +12 -0
- package/templates/harness-adapters/kiro/steering.md.njk +16 -0
- package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
- package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
- package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
- package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
- package/templates/knowledge/hooks.yaml.njk +4 -0
- package/templates/knowledge/knowledge.md.njk +53 -0
- package/templates/knowledge/mcp-servers.yaml.njk +2 -0
package/src/tutorial.ts
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { exists, readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { buildCommand } from "./build";
|
|
6
|
+
import { writeWizardResult } from "./file-writer";
|
|
7
|
+
import { newCommand } from "./new";
|
|
8
|
+
import { handleCancel, runWizard } from "./wizard";
|
|
9
|
+
|
|
10
|
+
/** A single step in the tutorial flow. */
|
|
11
|
+
export interface TutorialStep {
|
|
12
|
+
title: string;
|
|
13
|
+
explanation: string;
|
|
14
|
+
action?: () => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Suggested default values shown as placeholders during the tutorial wizard. */
|
|
18
|
+
export interface TutorialDefaults {
|
|
19
|
+
artifactName: string;
|
|
20
|
+
description: string;
|
|
21
|
+
keywords: string;
|
|
22
|
+
author: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const TUTORIAL_DEFAULTS: TutorialDefaults = {
|
|
26
|
+
artifactName: "hello-world",
|
|
27
|
+
description: "A sample artifact created during the Kanon tutorial",
|
|
28
|
+
keywords: "tutorial, sample, getting-started",
|
|
29
|
+
author: "Tutorial User",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build the ordered list of tutorial steps.
|
|
34
|
+
* Pure function — no side effects, easy to test.
|
|
35
|
+
*/
|
|
36
|
+
export function buildTutorialSteps(artifactName: string): TutorialStep[] {
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
title: "Welcome to Kanon",
|
|
40
|
+
explanation:
|
|
41
|
+
"Kanon helps you create knowledge artifacts — structured packages of expertise that AI coding assistants can use to help you better. Think of an artifact as a recipe card: it tells the AI what you know, when to use it, and how to apply it.",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: "Understanding artifact files",
|
|
45
|
+
explanation:
|
|
46
|
+
"Every artifact is a folder with three core files. knowledge.md holds your expertise written in plain text with a small metadata header. hooks.yaml defines optional automations that trigger when certain events happen (like editing a file). mcp-servers.yaml lists optional tool integrations the AI can call on your behalf.",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: "Create a sample artifact",
|
|
50
|
+
explanation: `Let's create a sample artifact called "${artifactName}" so you can see how the interactive wizard works. The wizard will ask you a few questions to fill in your artifact's details — just follow along and use the suggested values or type your own.`,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "Explore generated files",
|
|
54
|
+
explanation:
|
|
55
|
+
"Great job! The wizard just created your artifact files. Let's take a look at what was generated and how your answers were used to fill in each file. This will help you understand the structure so you can edit artifacts by hand later if you prefer.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "Build your artifact",
|
|
59
|
+
explanation: `Now let's compile your "${artifactName}" artifact. The build step takes your source files and produces output that AI coding tools can actually consume. This is how your knowledge gets delivered to the tools you use every day.`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
title: "Understanding build output",
|
|
63
|
+
explanation:
|
|
64
|
+
"The build created a dist/ folder containing compiled versions of your artifact — one for each AI coding tool you selected. Each tool has its own format, so Kanon translates your single source into the right shape for each target automatically.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: "You're all set!",
|
|
68
|
+
explanation:
|
|
69
|
+
"You've completed the Kanon tutorial. You created a sample artifact, explored its files, and built it for your AI tools. To create a real artifact with your own expertise, run `kanon new <name>`. Check out the documentation for advanced features like hooks and MCP server integrations.",
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Display an inline definition for a technical term using chalk styling.
|
|
76
|
+
*/
|
|
77
|
+
function _explainConcept(term: string, definition: string): void {
|
|
78
|
+
p.log.info(`${chalk.bold(term)}: ${definition}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Display a "Press Enter to continue" prompt, allowing the user to read at their own pace.
|
|
83
|
+
*/
|
|
84
|
+
async function waitForContinue(message?: string): Promise<void> {
|
|
85
|
+
const result = await p.text({
|
|
86
|
+
message: message ?? "Press Enter to continue",
|
|
87
|
+
defaultValue: "",
|
|
88
|
+
});
|
|
89
|
+
handleCancel(result);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Execute `kanon build` programmatically for the sample artifact and capture output.
|
|
94
|
+
* Catches errors gracefully so the tutorial can continue.
|
|
95
|
+
*/
|
|
96
|
+
async function runTutorialBuild(_artifactName: string): Promise<void> {
|
|
97
|
+
try {
|
|
98
|
+
await buildCommand({});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
101
|
+
p.log.warning(
|
|
102
|
+
`Build encountered an issue: ${msg}\nTry running \`forge validate\` to diagnose the problem.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Display the welcome message explaining Kanon and artifacts.
|
|
109
|
+
* Uses plain, non-technical language aimed at researchers.
|
|
110
|
+
*/
|
|
111
|
+
export function showWelcome(): void {
|
|
112
|
+
p.log.info(
|
|
113
|
+
`${chalk.bold("Welcome to Kanon!")} — a tool for packaging your expertise so AI coding assistants can use it.`,
|
|
114
|
+
);
|
|
115
|
+
p.note(
|
|
116
|
+
"An artifact is a small, structured package of knowledge.\n" +
|
|
117
|
+
"Think of it like a recipe card: it tells the AI what you know,\n" +
|
|
118
|
+
"when to use that knowledge, and how to apply it.\n\n" +
|
|
119
|
+
"No coding experience required — just your expertise and a few minutes.",
|
|
120
|
+
"What is an artifact?",
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Display a progress indicator showing the current step and total number of steps.
|
|
126
|
+
*/
|
|
127
|
+
export function showProgress(current: number, total: number): void {
|
|
128
|
+
p.log.step(`Step ${current} of ${total}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Explain what each generated file does, reading actual file contents and annotating them.
|
|
133
|
+
*/
|
|
134
|
+
export async function explainGeneratedFiles(
|
|
135
|
+
artifactDir: string,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
const files = [
|
|
138
|
+
{
|
|
139
|
+
name: "knowledge.md",
|
|
140
|
+
description:
|
|
141
|
+
"This is your main knowledge file. The top section (between the --- lines) is metadata\n" +
|
|
142
|
+
"that helps AI tools understand what your artifact is about. The content below is your\n" +
|
|
143
|
+
"actual expertise written in plain text.",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: "hooks.yaml",
|
|
147
|
+
description:
|
|
148
|
+
"This file defines automations — things that happen automatically when certain events\n" +
|
|
149
|
+
"occur (like editing a file). If you didn't add any hooks, this file is empty and that's fine.",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "mcp-servers.yaml",
|
|
153
|
+
description:
|
|
154
|
+
"This file lists tool integrations the AI can call on your behalf.\n" +
|
|
155
|
+
"If you didn't add any servers, this file is empty and that's fine.",
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
for (const file of files) {
|
|
160
|
+
const filePath = join(artifactDir, file.name);
|
|
161
|
+
let content = "";
|
|
162
|
+
try {
|
|
163
|
+
content = await readFile(filePath, "utf-8");
|
|
164
|
+
} catch {
|
|
165
|
+
content = "(file not found)";
|
|
166
|
+
}
|
|
167
|
+
p.note(
|
|
168
|
+
`${file.description}\n\n--- ${file.name} ---\n${content}`,
|
|
169
|
+
file.name,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Explain the build output location and how harnesses consume it.
|
|
176
|
+
*/
|
|
177
|
+
export function explainBuildOutput(): void {
|
|
178
|
+
p.note(
|
|
179
|
+
"The build output lives in the dist/ folder.\n" +
|
|
180
|
+
"Each AI coding tool (harness) gets its own compiled version of your artifact.\n" +
|
|
181
|
+
"Kanon translates your single source into the right format for each target\n" +
|
|
182
|
+
"automatically — you only need to maintain one set of source files.",
|
|
183
|
+
"Build output",
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Display the completion summary and suggest next steps.
|
|
189
|
+
*/
|
|
190
|
+
export function showCompletion(): void {
|
|
191
|
+
p.note(
|
|
192
|
+
"You've completed the Kanon tutorial! Here's what you accomplished:\n\n" +
|
|
193
|
+
" • Created a sample artifact with the interactive wizard\n" +
|
|
194
|
+
" • Explored the generated files and understood their structure\n" +
|
|
195
|
+
" • Built the artifact for your AI coding tools\n\n" +
|
|
196
|
+
"Next steps:\n" +
|
|
197
|
+
" • Run `kanon new <name>` to create a real artifact with your own expertise\n" +
|
|
198
|
+
" • Check out the documentation for advanced features like hooks and MCP servers",
|
|
199
|
+
"Tutorial complete!",
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Check if the sample artifact already exists and prompt for resolution.
|
|
205
|
+
* Returns the artifact name to use (original or user-chosen alternative).
|
|
206
|
+
*/
|
|
207
|
+
export async function resolveArtifactName(
|
|
208
|
+
defaultName: string,
|
|
209
|
+
): Promise<string> {
|
|
210
|
+
const artifactDir = join("knowledge", defaultName);
|
|
211
|
+
|
|
212
|
+
if (!(await exists(artifactDir))) {
|
|
213
|
+
return defaultName;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const overwrite = await p.confirm({
|
|
217
|
+
message: `An artifact named "${defaultName}" already exists. Overwrite it?`,
|
|
218
|
+
});
|
|
219
|
+
handleCancel(overwrite);
|
|
220
|
+
|
|
221
|
+
if (overwrite) {
|
|
222
|
+
await rm(artifactDir, { recursive: true, force: true });
|
|
223
|
+
return defaultName;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const newName = await p.text({
|
|
227
|
+
message: "Choose a different name for the sample artifact",
|
|
228
|
+
validate: (val) => {
|
|
229
|
+
if (!val || val.trim().length === 0) return "Name cannot be empty";
|
|
230
|
+
return undefined;
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
handleCancel(newName);
|
|
234
|
+
|
|
235
|
+
return newName as string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Entry point for `kanon tutorial`.
|
|
240
|
+
* Runs the full guided walkthrough.
|
|
241
|
+
*/
|
|
242
|
+
export async function tutorialCommand(): Promise<void> {
|
|
243
|
+
try {
|
|
244
|
+
p.intro("Kanon Tutorial");
|
|
245
|
+
|
|
246
|
+
const artifactName = await resolveArtifactName(
|
|
247
|
+
TUTORIAL_DEFAULTS.artifactName,
|
|
248
|
+
);
|
|
249
|
+
const displayName = artifactName
|
|
250
|
+
.split("-")
|
|
251
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
252
|
+
.join(" ");
|
|
253
|
+
const artifactDir = join("knowledge", artifactName);
|
|
254
|
+
|
|
255
|
+
showWelcome();
|
|
256
|
+
await waitForContinue();
|
|
257
|
+
|
|
258
|
+
const steps = buildTutorialSteps(artifactName);
|
|
259
|
+
|
|
260
|
+
// Wire up actions for specific steps
|
|
261
|
+
steps[2].action = async () => {
|
|
262
|
+
await newCommand(artifactName, { yes: true });
|
|
263
|
+
const result = await runWizard(artifactName, displayName);
|
|
264
|
+
await writeWizardResult(artifactDir, result);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
steps[3].action = async () => {
|
|
268
|
+
await explainGeneratedFiles(artifactDir);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
steps[4].action = async () => {
|
|
272
|
+
await runTutorialBuild(artifactName);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
steps[5].action = async () => {
|
|
276
|
+
explainBuildOutput();
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Iterate through steps
|
|
280
|
+
for (let i = 0; i < steps.length; i++) {
|
|
281
|
+
const step = steps[i];
|
|
282
|
+
showProgress(i + 1, steps.length);
|
|
283
|
+
p.log.info(step.explanation);
|
|
284
|
+
|
|
285
|
+
if (step.action) {
|
|
286
|
+
await step.action();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (i < steps.length - 1) {
|
|
290
|
+
await waitForContinue();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
showCompletion();
|
|
295
|
+
p.outro("Happy forging!");
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error instanceof Error && error.message.includes("cancel")) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
301
|
+
p.log.error(
|
|
302
|
+
`Tutorial encountered an error: ${msg}\nTry running \`forge validate\` to diagnose the problem.`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|