kiro-kit 0.2.2 → 0.3.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/README.md +35 -0
- package/dist/index.js +771 -116
- package/dist/index.js.map +1 -1
- package/dist/presets/backend/hooks/api-schema-validate.js +75 -0
- package/dist/presets/backend/hooks/endpoint-test-coverage.js +77 -0
- package/dist/presets/backend/hooks/migration-safety-check.js +68 -0
- package/dist/presets/backend/manifest.json +48 -35
- package/dist/presets/backend/powers.json +40 -0
- package/dist/presets/backend/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/backend/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/backend/skills/kiro/references/commands.md +61 -0
- package/dist/presets/backend/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/backend/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/backend/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/backend/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/backend/skills/kiro/references/skills.md +55 -0
- package/dist/presets/backend/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/backend/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/backend/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/backend/skills/kiro/skill.md +194 -0
- package/dist/presets/data-ai/hooks/data-drift-check.js +77 -0
- package/dist/presets/data-ai/hooks/experiment-log.js +87 -0
- package/dist/presets/data-ai/hooks/model-card-update.js +99 -0
- package/dist/presets/data-ai/manifest.json +48 -35
- package/dist/presets/data-ai/powers.json +34 -0
- package/dist/presets/data-ai/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/data-ai/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/data-ai/skills/kiro/references/commands.md +61 -0
- package/dist/presets/data-ai/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/data-ai/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/data-ai/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/data-ai/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/data-ai/skills/kiro/references/skills.md +55 -0
- package/dist/presets/data-ai/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/data-ai/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/data-ai/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/data-ai/skills/kiro/skill.md +194 -0
- package/dist/presets/devops/hooks/container-scan.js +73 -0
- package/dist/presets/devops/hooks/cost-estimation.js +69 -0
- package/dist/presets/devops/hooks/terraform-plan-review.js +67 -0
- package/dist/presets/devops/manifest.json +48 -35
- package/dist/presets/devops/powers.json +40 -0
- package/dist/presets/devops/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/devops/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/devops/skills/kiro/references/commands.md +61 -0
- package/dist/presets/devops/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/devops/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/devops/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/devops/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/devops/skills/kiro/references/skills.md +55 -0
- package/dist/presets/devops/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/devops/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/devops/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/devops/skills/kiro/skill.md +194 -0
- package/dist/presets/frontend/commands/test.md +1 -1
- package/dist/presets/frontend/hooks/accessibility-check.js +76 -0
- package/dist/presets/frontend/hooks/bundle-size-guard.js +71 -0
- package/dist/presets/frontend/hooks/component-test-reminder.js +71 -0
- package/dist/presets/frontend/manifest.json +48 -35
- package/dist/presets/frontend/powers.json +34 -0
- package/dist/presets/frontend/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/frontend/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/frontend/skills/kiro/references/commands.md +61 -0
- package/dist/presets/frontend/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/frontend/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/frontend/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/frontend/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/frontend/skills/kiro/references/skills.md +55 -0
- package/dist/presets/frontend/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/frontend/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/frontend/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/frontend/skills/kiro/skill.md +194 -0
- package/dist/presets/fullstack/hooks/api-client-gen.js +69 -0
- package/dist/presets/fullstack/hooks/deployment-readiness.js +73 -0
- package/dist/presets/fullstack/hooks/type-sync-check.js +69 -0
- package/dist/presets/fullstack/manifest.json +48 -35
- package/dist/presets/fullstack/powers.json +46 -0
- package/dist/presets/fullstack/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/fullstack/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/fullstack/skills/kiro/references/commands.md +61 -0
- package/dist/presets/fullstack/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/fullstack/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/fullstack/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/fullstack/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/fullstack/skills/kiro/references/skills.md +55 -0
- package/dist/presets/fullstack/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/fullstack/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/fullstack/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/fullstack/skills/kiro/skill.md +194 -0
- package/dist/presets/mobile/hooks/asset-optimization.js +58 -0
- package/dist/presets/mobile/hooks/platform-parity-check.js +73 -0
- package/dist/presets/mobile/hooks/release-checklist.js +83 -0
- package/dist/presets/mobile/manifest.json +48 -35
- package/dist/presets/mobile/powers.json +34 -0
- package/dist/presets/mobile/skills/kiro/references/autonomy-modes.md +26 -0
- package/dist/presets/mobile/skills/kiro/references/best-practices.md +48 -0
- package/dist/presets/mobile/skills/kiro/references/commands.md +61 -0
- package/dist/presets/mobile/skills/kiro/references/configuration.md +41 -0
- package/dist/presets/mobile/skills/kiro/references/getting-started.md +45 -0
- package/dist/presets/mobile/skills/kiro/references/hooks.md +99 -0
- package/dist/presets/mobile/skills/kiro/references/mcp-integration.md +53 -0
- package/dist/presets/mobile/skills/kiro/references/skills.md +55 -0
- package/dist/presets/mobile/skills/kiro/references/specs-workflow.md +62 -0
- package/dist/presets/mobile/skills/kiro/references/steering-files.md +84 -0
- package/dist/presets/mobile/skills/kiro/references/troubleshooting.md +42 -0
- package/dist/presets/mobile/skills/kiro/skill.md +194 -0
- package/package.json +1 -1
- package/dist/presets/backend/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/backend/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/backend/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/backend/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/backend/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/backend/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/backend/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/backend/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/backend/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/backend/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/backend/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/backend/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/backend/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/backend/skills/claude-code/skill.md +0 -194
- package/dist/presets/data-ai/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/data-ai/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/data-ai/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/data-ai/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/data-ai/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/data-ai/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/data-ai/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/data-ai/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/data-ai/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/data-ai/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/data-ai/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/data-ai/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/data-ai/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/data-ai/skills/claude-code/skill.md +0 -194
- package/dist/presets/devops/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/devops/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/devops/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/devops/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/devops/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/devops/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/devops/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/devops/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/devops/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/devops/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/devops/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/devops/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/devops/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/devops/skills/claude-code/skill.md +0 -194
- package/dist/presets/frontend/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/frontend/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/frontend/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/frontend/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/frontend/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/frontend/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/frontend/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/frontend/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/frontend/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/frontend/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/frontend/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/frontend/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/frontend/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/frontend/skills/claude-code/skill.md +0 -194
- package/dist/presets/fullstack/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/fullstack/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/fullstack/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/fullstack/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/fullstack/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/fullstack/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/fullstack/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/fullstack/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/fullstack/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/fullstack/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/fullstack/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/fullstack/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/fullstack/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/fullstack/skills/claude-code/skill.md +0 -194
- package/dist/presets/mobile/skills/claude-code/references/advanced-features.md +0 -399
- package/dist/presets/mobile/skills/claude-code/references/agent-skills.md +0 -399
- package/dist/presets/mobile/skills/claude-code/references/api-reference.md +0 -498
- package/dist/presets/mobile/skills/claude-code/references/best-practices.md +0 -447
- package/dist/presets/mobile/skills/claude-code/references/cicd-integration.md +0 -428
- package/dist/presets/mobile/skills/claude-code/references/configuration.md +0 -480
- package/dist/presets/mobile/skills/claude-code/references/enterprise-features.md +0 -472
- package/dist/presets/mobile/skills/claude-code/references/getting-started.md +0 -252
- package/dist/presets/mobile/skills/claude-code/references/hooks-and-plugins.md +0 -441
- package/dist/presets/mobile/skills/claude-code/references/ide-integration.md +0 -316
- package/dist/presets/mobile/skills/claude-code/references/mcp-integration.md +0 -386
- package/dist/presets/mobile/skills/claude-code/references/slash-commands.md +0 -489
- package/dist/presets/mobile/skills/claude-code/references/troubleshooting.md +0 -456
- package/dist/presets/mobile/skills/claude-code/skill.md +0 -194
package/dist/index.js
CHANGED
|
@@ -6,10 +6,10 @@ import process13 from "process";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/commands/init.ts
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import fs14 from "fs";
|
|
10
|
+
import path13 from "path";
|
|
11
11
|
import process5 from "process";
|
|
12
|
-
import
|
|
12
|
+
import readline2 from "readline";
|
|
13
13
|
import crypto3 from "crypto";
|
|
14
14
|
|
|
15
15
|
// src/core/PresetLoader.ts
|
|
@@ -81,7 +81,8 @@ var ArtifactTypeSchema = z.enum([
|
|
|
81
81
|
"docs",
|
|
82
82
|
"doc",
|
|
83
83
|
"config",
|
|
84
|
-
"other"
|
|
84
|
+
"other",
|
|
85
|
+
"powers"
|
|
85
86
|
]);
|
|
86
87
|
var PresetNameSchema = z.enum([
|
|
87
88
|
"frontend",
|
|
@@ -484,7 +485,7 @@ function read2(workspaceRoot) {
|
|
|
484
485
|
}
|
|
485
486
|
}
|
|
486
487
|
function mergePresets(existing, newPresets) {
|
|
487
|
-
const merged = [...existing.presets];
|
|
488
|
+
const merged = [...existing.presets || []];
|
|
488
489
|
for (const p of newPresets) {
|
|
489
490
|
const idx = merged.findIndex((m) => m.name === p.name);
|
|
490
491
|
if (idx >= 0) {
|
|
@@ -665,6 +666,564 @@ function safePathInside(workspaceRoot, target) {
|
|
|
665
666
|
return resolved.startsWith(root + path8.sep) || resolved === root;
|
|
666
667
|
}
|
|
667
668
|
|
|
669
|
+
// src/core/PowersLoader.ts
|
|
670
|
+
import fs10 from "fs";
|
|
671
|
+
import path9 from "path";
|
|
672
|
+
import { z as z2 } from "zod";
|
|
673
|
+
var PowerTierSchema = z2.enum(["essential", "recommended", "optional"]);
|
|
674
|
+
var PowerEntrySchema = z2.object({
|
|
675
|
+
name: z2.string().min(1),
|
|
676
|
+
url: z2.string().url(),
|
|
677
|
+
description: z2.string().min(1),
|
|
678
|
+
tier: PowerTierSchema
|
|
679
|
+
});
|
|
680
|
+
var PowersConfigSchema = z2.object({
|
|
681
|
+
powers: z2.array(PowerEntrySchema)
|
|
682
|
+
});
|
|
683
|
+
var POWERS_FILENAME = "powers.json";
|
|
684
|
+
var TIER_PRIORITY = {
|
|
685
|
+
essential: 0,
|
|
686
|
+
recommended: 1,
|
|
687
|
+
optional: 2
|
|
688
|
+
};
|
|
689
|
+
function loadPowers(presetDir) {
|
|
690
|
+
const filePath = path9.join(presetDir, POWERS_FILENAME);
|
|
691
|
+
if (!fs10.existsSync(filePath)) {
|
|
692
|
+
return { ok: true, powers: [] };
|
|
693
|
+
}
|
|
694
|
+
let raw;
|
|
695
|
+
try {
|
|
696
|
+
raw = fs10.readFileSync(filePath, "utf-8");
|
|
697
|
+
} catch (err) {
|
|
698
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
699
|
+
return { ok: false, powers: [], error: `Failed to read ${POWERS_FILENAME}: ${message}` };
|
|
700
|
+
}
|
|
701
|
+
let parsed;
|
|
702
|
+
try {
|
|
703
|
+
parsed = JSON.parse(raw);
|
|
704
|
+
} catch {
|
|
705
|
+
return { ok: false, powers: [], error: `${POWERS_FILENAME} is not valid JSON.` };
|
|
706
|
+
}
|
|
707
|
+
const result = PowersConfigSchema.safeParse(parsed);
|
|
708
|
+
if (!result.success) {
|
|
709
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
710
|
+
return { ok: false, powers: [], error: `${POWERS_FILENAME} schema validation failed: ${issues}` };
|
|
711
|
+
}
|
|
712
|
+
return { ok: true, powers: result.data.powers };
|
|
713
|
+
}
|
|
714
|
+
function mergePowers(configs) {
|
|
715
|
+
const seen = /* @__PURE__ */ new Map();
|
|
716
|
+
for (const entries of configs) {
|
|
717
|
+
for (const entry of entries) {
|
|
718
|
+
const existing = seen.get(entry.name);
|
|
719
|
+
if (!existing) {
|
|
720
|
+
seen.set(entry.name, entry);
|
|
721
|
+
} else if (TIER_PRIORITY[entry.tier] < TIER_PRIORITY[existing.tier]) {
|
|
722
|
+
seen.set(entry.name, entry);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return Array.from(seen.values());
|
|
727
|
+
}
|
|
728
|
+
function filterByTier(powers, tiers) {
|
|
729
|
+
const tierSet = new Set(tiers);
|
|
730
|
+
return powers.filter((p) => tierSet.has(p.tier));
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/core/MCPConfigurator.ts
|
|
734
|
+
import fs11 from "fs";
|
|
735
|
+
import path10 from "path";
|
|
736
|
+
var MCP_FILENAME = ".mcp.json";
|
|
737
|
+
var SERVER_DEFINITIONS = {
|
|
738
|
+
filesystem: {
|
|
739
|
+
command: "npx",
|
|
740
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
|
741
|
+
requiresCredentials: false
|
|
742
|
+
},
|
|
743
|
+
git: {
|
|
744
|
+
command: "npx",
|
|
745
|
+
args: ["-y", "@modelcontextprotocol/server-git", "--repository", "."],
|
|
746
|
+
requiresCredentials: false
|
|
747
|
+
},
|
|
748
|
+
fetch: {
|
|
749
|
+
command: "npx",
|
|
750
|
+
args: ["-y", "@modelcontextprotocol/server-fetch"],
|
|
751
|
+
requiresCredentials: false
|
|
752
|
+
},
|
|
753
|
+
playwright: {
|
|
754
|
+
command: "npx",
|
|
755
|
+
args: ["-y", "@modelcontextprotocol/server-playwright"],
|
|
756
|
+
requiresCredentials: false
|
|
757
|
+
},
|
|
758
|
+
postgres: {
|
|
759
|
+
command: "npx",
|
|
760
|
+
args: ["-y", "@modelcontextprotocol/server-postgres"],
|
|
761
|
+
env: { POSTGRES_URL: "${POSTGRES_URL}" },
|
|
762
|
+
requiresCredentials: true,
|
|
763
|
+
credentialEnvVars: ["POSTGRES_URL"]
|
|
764
|
+
},
|
|
765
|
+
docker: {
|
|
766
|
+
command: "npx",
|
|
767
|
+
args: ["-y", "@modelcontextprotocol/server-docker"],
|
|
768
|
+
env: { DOCKER_HOST: "${DOCKER_HOST}" },
|
|
769
|
+
requiresCredentials: true,
|
|
770
|
+
credentialEnvVars: ["DOCKER_HOST"]
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
var PRESET_SERVERS = {
|
|
774
|
+
frontend: { default: ["filesystem", "git", "fetch", "playwright"], optional: [] },
|
|
775
|
+
backend: { default: ["filesystem", "git", "fetch"], optional: ["postgres", "docker"] },
|
|
776
|
+
fullstack: { default: ["filesystem", "git", "fetch", "playwright"], optional: ["postgres", "docker"] },
|
|
777
|
+
mobile: { default: ["filesystem", "git", "fetch"], optional: [] },
|
|
778
|
+
devops: { default: ["filesystem", "git", "fetch"], optional: ["docker", "postgres"] },
|
|
779
|
+
"data-ai": { default: ["filesystem", "git", "fetch"], optional: ["postgres"] }
|
|
780
|
+
};
|
|
781
|
+
function getMCPConfig(presetName) {
|
|
782
|
+
const mapping = PRESET_SERVERS[presetName];
|
|
783
|
+
if (!mapping) {
|
|
784
|
+
return { servers: {} };
|
|
785
|
+
}
|
|
786
|
+
const servers = {};
|
|
787
|
+
for (const name of mapping.default) {
|
|
788
|
+
const def = SERVER_DEFINITIONS[name];
|
|
789
|
+
if (def) {
|
|
790
|
+
servers[name] = { ...def };
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
for (const name of mapping.optional) {
|
|
794
|
+
const def = SERVER_DEFINITIONS[name];
|
|
795
|
+
if (def) {
|
|
796
|
+
servers[name] = { ...def };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return { servers };
|
|
800
|
+
}
|
|
801
|
+
function mergeMCPConfig(existing, incoming) {
|
|
802
|
+
const existingServers = existing && typeof existing === "object" && existing !== null ? existing.mcpServers ?? {} : {};
|
|
803
|
+
const merged = { ...existingServers };
|
|
804
|
+
for (const [name, entry] of Object.entries(incoming.servers)) {
|
|
805
|
+
const outputKey = entry.requiresCredentials ? `_disabled_${name}` : name;
|
|
806
|
+
if (merged[name] !== void 0 || merged[`_disabled_${name}`] !== void 0) {
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
const serverObj = { command: entry.command };
|
|
810
|
+
if (entry.args && entry.args.length > 0) {
|
|
811
|
+
serverObj.args = entry.args;
|
|
812
|
+
}
|
|
813
|
+
if (entry.env && Object.keys(entry.env).length > 0) {
|
|
814
|
+
serverObj.env = entry.env;
|
|
815
|
+
}
|
|
816
|
+
if (entry.requiresCredentials) {
|
|
817
|
+
serverObj._comment = `Requires ${entry.credentialEnvVars?.join(", ") ?? "credentials"} environment variable. Remove '_disabled_' prefix to enable.`;
|
|
818
|
+
}
|
|
819
|
+
merged[outputKey] = serverObj;
|
|
820
|
+
}
|
|
821
|
+
const result = { ...existing ?? {} };
|
|
822
|
+
result.mcpServers = merged;
|
|
823
|
+
return result;
|
|
824
|
+
}
|
|
825
|
+
function writeMCPConfig(workspaceRoot, config) {
|
|
826
|
+
const filePath = path10.join(workspaceRoot, MCP_FILENAME);
|
|
827
|
+
const content = JSON.stringify(config, null, 2) + "\n";
|
|
828
|
+
try {
|
|
829
|
+
fs11.writeFileSync(filePath, content, "utf-8");
|
|
830
|
+
} catch (err) {
|
|
831
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
832
|
+
throw new KKError(
|
|
833
|
+
ErrorCodes.MCP_MERGE_CONFLICT,
|
|
834
|
+
`Failed to write ${MCP_FILENAME}: ${message}`,
|
|
835
|
+
"Check file permissions and ensure the directory exists."
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/core/SetupGuideGenerator.ts
|
|
841
|
+
import fs12 from "fs";
|
|
842
|
+
import path11 from "path";
|
|
843
|
+
var SETUP_GUIDE_FILENAME = "POWERS-SETUP.md";
|
|
844
|
+
var KIRO_DIR = ".kiro";
|
|
845
|
+
var TIER_ORDER = [
|
|
846
|
+
{ tier: "essential", heading: "Essential Powers (install these first)" },
|
|
847
|
+
{ tier: "recommended", heading: "Recommended Powers" },
|
|
848
|
+
{ tier: "optional", heading: "Optional Powers" }
|
|
849
|
+
];
|
|
850
|
+
function generateSetupGuide(options) {
|
|
851
|
+
const { powers, presetNames, mcpServers } = options;
|
|
852
|
+
const lines = [];
|
|
853
|
+
lines.push("# Kiro Powers Setup Guide");
|
|
854
|
+
lines.push("");
|
|
855
|
+
lines.push(`## Presets: ${presetNames.join(", ")}`);
|
|
856
|
+
lines.push("");
|
|
857
|
+
let counter = 0;
|
|
858
|
+
for (const { tier, heading } of TIER_ORDER) {
|
|
859
|
+
const tierPowers = powers.filter((p) => p.tier === tier);
|
|
860
|
+
if (tierPowers.length === 0) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
lines.push(`## ${heading}`);
|
|
864
|
+
lines.push("");
|
|
865
|
+
for (const power of tierPowers) {
|
|
866
|
+
counter += 1;
|
|
867
|
+
lines.push(`### ${counter}. ${power.name}`);
|
|
868
|
+
lines.push(`- URL: ${power.url}`);
|
|
869
|
+
lines.push(`- Description: ${power.description}`);
|
|
870
|
+
lines.push(`- How to install: Open Kiro IDE > Powers panel > Search "${power.name}" > Click Install`);
|
|
871
|
+
lines.push("");
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const serverNames = Object.keys(mcpServers);
|
|
875
|
+
if (serverNames.length > 0) {
|
|
876
|
+
lines.push("## MCP Servers (auto-configured)");
|
|
877
|
+
lines.push("");
|
|
878
|
+
lines.push("The following MCP servers have been configured in `.mcp.json`:");
|
|
879
|
+
lines.push("");
|
|
880
|
+
for (const name of serverNames) {
|
|
881
|
+
const entry = mcpServers[name];
|
|
882
|
+
if (entry.requiresCredentials) {
|
|
883
|
+
lines.push(`- **${name}** (disabled, requires credentials)`);
|
|
884
|
+
} else {
|
|
885
|
+
lines.push(`- **${name}** (enabled)`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
lines.push("");
|
|
889
|
+
lines.push("To enable disabled servers, set the required environment variables");
|
|
890
|
+
lines.push("and remove the `_disabled_` prefix from the server key in `.mcp.json`.");
|
|
891
|
+
lines.push("");
|
|
892
|
+
}
|
|
893
|
+
return lines.join("\n");
|
|
894
|
+
}
|
|
895
|
+
function writeSetupGuide(workspaceRoot, content) {
|
|
896
|
+
const dirPath = path11.join(workspaceRoot, KIRO_DIR);
|
|
897
|
+
const filePath = path11.join(dirPath, SETUP_GUIDE_FILENAME);
|
|
898
|
+
try {
|
|
899
|
+
if (!fs12.existsSync(dirPath)) {
|
|
900
|
+
fs12.mkdirSync(dirPath, { recursive: true });
|
|
901
|
+
}
|
|
902
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
903
|
+
} catch (err) {
|
|
904
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
905
|
+
throw new KKError(
|
|
906
|
+
ErrorCodes.MCP_MERGE_CONFLICT,
|
|
907
|
+
`Failed to write ${SETUP_GUIDE_FILENAME}: ${message}`,
|
|
908
|
+
"Check file permissions and ensure the .kiro directory is writable."
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/core/EnvTemplateGenerator.ts
|
|
914
|
+
import fs13 from "fs";
|
|
915
|
+
import path12 from "path";
|
|
916
|
+
var ENV_FILENAME = ".env.example";
|
|
917
|
+
function collectEnvVars(mcpConfig, powers) {
|
|
918
|
+
const vars = [];
|
|
919
|
+
for (const [name, entry] of Object.entries(mcpConfig.servers)) {
|
|
920
|
+
if (!entry.requiresCredentials) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
const envVars = entry.credentialEnvVars ?? [];
|
|
924
|
+
const envMap = entry.env ?? {};
|
|
925
|
+
for (const key of envVars) {
|
|
926
|
+
const placeholder = envMap[key] ?? `your-${key.toLowerCase().replace(/_/g, "-")}`;
|
|
927
|
+
vars.push({
|
|
928
|
+
key,
|
|
929
|
+
placeholder,
|
|
930
|
+
comment: `Required by ${name} MCP server`,
|
|
931
|
+
service: `${name} MCP Server`
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
for (const power of powers) {
|
|
936
|
+
if (power.tier === "essential" || power.tier === "recommended") {
|
|
937
|
+
const knownPowerEnvVars = getKnownPowerEnvVars(power.name);
|
|
938
|
+
for (const envVar of knownPowerEnvVars) {
|
|
939
|
+
vars.push(envVar);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return vars;
|
|
944
|
+
}
|
|
945
|
+
function parseExistingEnv(content) {
|
|
946
|
+
const keys = /* @__PURE__ */ new Set();
|
|
947
|
+
for (const line of content.split("\n")) {
|
|
948
|
+
const trimmed = line.trim();
|
|
949
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
const match = trimmed.match(/^#?\s*([A-Z_][A-Z0-9_]*)=/);
|
|
953
|
+
if (match) {
|
|
954
|
+
keys.add(match[1]);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return keys;
|
|
958
|
+
}
|
|
959
|
+
function generateEnvTemplate(existing, newVars) {
|
|
960
|
+
const existingKeys = existing ? parseExistingEnv(existing) : /* @__PURE__ */ new Set();
|
|
961
|
+
const varsToAdd = newVars.filter((v) => !existingKeys.has(v.key));
|
|
962
|
+
if (varsToAdd.length === 0) {
|
|
963
|
+
return existing ?? "";
|
|
964
|
+
}
|
|
965
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const v of varsToAdd) {
|
|
967
|
+
const group = grouped.get(v.service) ?? [];
|
|
968
|
+
group.push(v);
|
|
969
|
+
grouped.set(v.service, group);
|
|
970
|
+
}
|
|
971
|
+
const lines = [];
|
|
972
|
+
for (const [service, vars] of grouped) {
|
|
973
|
+
lines.push(`# ${service}`);
|
|
974
|
+
for (const v of vars) {
|
|
975
|
+
lines.push(`# ${v.comment}`);
|
|
976
|
+
lines.push(`# ${v.key}=${v.placeholder}`);
|
|
977
|
+
}
|
|
978
|
+
lines.push("");
|
|
979
|
+
}
|
|
980
|
+
const newContent = lines.join("\n");
|
|
981
|
+
if (!existing || existing.trim() === "") {
|
|
982
|
+
return newContent;
|
|
983
|
+
}
|
|
984
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
985
|
+
return existing + separator + "\n" + newContent;
|
|
986
|
+
}
|
|
987
|
+
function readExistingEnv(workspaceRoot) {
|
|
988
|
+
const filePath = path12.join(workspaceRoot, ENV_FILENAME);
|
|
989
|
+
try {
|
|
990
|
+
return fs13.readFileSync(filePath, "utf-8");
|
|
991
|
+
} catch {
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
function writeEnvTemplate(workspaceRoot, content) {
|
|
996
|
+
const filePath = path12.join(workspaceRoot, ENV_FILENAME);
|
|
997
|
+
try {
|
|
998
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1001
|
+
throw new KKError(
|
|
1002
|
+
ErrorCodes.MCP_MERGE_CONFLICT,
|
|
1003
|
+
`Failed to write ${ENV_FILENAME}: ${message}`,
|
|
1004
|
+
"Check file permissions or create the file manually."
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function getKnownPowerEnvVars(powerName) {
|
|
1009
|
+
const mapping = {
|
|
1010
|
+
Supabase: [
|
|
1011
|
+
{
|
|
1012
|
+
key: "SUPABASE_URL",
|
|
1013
|
+
placeholder: "https://your-project.supabase.co",
|
|
1014
|
+
comment: "Supabase project URL",
|
|
1015
|
+
service: "Supabase Power"
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
key: "SUPABASE_ANON_KEY",
|
|
1019
|
+
placeholder: "your-anon-key",
|
|
1020
|
+
comment: "Supabase anonymous key",
|
|
1021
|
+
service: "Supabase Power"
|
|
1022
|
+
}
|
|
1023
|
+
],
|
|
1024
|
+
Firebase: [
|
|
1025
|
+
{
|
|
1026
|
+
key: "FIREBASE_PROJECT_ID",
|
|
1027
|
+
placeholder: "your-project-id",
|
|
1028
|
+
comment: "Firebase project ID",
|
|
1029
|
+
service: "Firebase Power"
|
|
1030
|
+
}
|
|
1031
|
+
],
|
|
1032
|
+
Stripe: [
|
|
1033
|
+
{
|
|
1034
|
+
key: "STRIPE_SECRET_KEY",
|
|
1035
|
+
placeholder: "sk_test_your-key",
|
|
1036
|
+
comment: "Stripe secret key",
|
|
1037
|
+
service: "Stripe Power"
|
|
1038
|
+
}
|
|
1039
|
+
],
|
|
1040
|
+
Datadog: [
|
|
1041
|
+
{
|
|
1042
|
+
key: "DD_API_KEY",
|
|
1043
|
+
placeholder: "your-datadog-api-key",
|
|
1044
|
+
comment: "Datadog API key",
|
|
1045
|
+
service: "Datadog Power"
|
|
1046
|
+
}
|
|
1047
|
+
],
|
|
1048
|
+
Neon: [
|
|
1049
|
+
{
|
|
1050
|
+
key: "NEON_DATABASE_URL",
|
|
1051
|
+
placeholder: "postgresql://user:password@host/dbname",
|
|
1052
|
+
comment: "Neon database connection string",
|
|
1053
|
+
service: "Neon Power"
|
|
1054
|
+
}
|
|
1055
|
+
]
|
|
1056
|
+
};
|
|
1057
|
+
return mapping[powerName] ?? [];
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/prompts/PowersPrompter.ts
|
|
1061
|
+
import readline from "readline";
|
|
1062
|
+
var TIER_ORDER2 = ["essential", "recommended", "optional"];
|
|
1063
|
+
var TIER_LABELS = {
|
|
1064
|
+
essential: "Essential",
|
|
1065
|
+
recommended: "Recommended",
|
|
1066
|
+
optional: "Optional"
|
|
1067
|
+
};
|
|
1068
|
+
var TIER_OPTIONS = [
|
|
1069
|
+
{ label: "Essential only", tiers: ["essential"] },
|
|
1070
|
+
{ label: "Essential + Recommended", tiers: ["essential", "recommended"] },
|
|
1071
|
+
{ label: "All (Essential + Recommended + Optional)", tiers: ["essential", "recommended", "optional"] },
|
|
1072
|
+
{ label: "None (skip Powers)", tiers: [] }
|
|
1073
|
+
];
|
|
1074
|
+
async function promptPowersTier(powers, flags) {
|
|
1075
|
+
if (powers.length === 0) {
|
|
1076
|
+
return { selectedTiers: [], confirmMCP: false };
|
|
1077
|
+
}
|
|
1078
|
+
if (flags.powersFlag === "none") {
|
|
1079
|
+
return { selectedTiers: [], confirmMCP: false };
|
|
1080
|
+
}
|
|
1081
|
+
if (flags.powersFlag === "all") {
|
|
1082
|
+
return { selectedTiers: ["essential", "recommended", "optional"], confirmMCP: true };
|
|
1083
|
+
}
|
|
1084
|
+
if (flags.yes === true) {
|
|
1085
|
+
return { selectedTiers: ["essential", "recommended"], confirmMCP: true };
|
|
1086
|
+
}
|
|
1087
|
+
return interactiveTierPrompt(powers);
|
|
1088
|
+
}
|
|
1089
|
+
function displayPowersRecommendations(powers, quiet) {
|
|
1090
|
+
if (quiet || powers.length === 0) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
process.stdout.write("\n");
|
|
1094
|
+
process.stdout.write(color.bold("Recommended Kiro Powers:") + "\n");
|
|
1095
|
+
for (const tier of TIER_ORDER2) {
|
|
1096
|
+
const tierPowers = powers.filter((p) => p.tier === tier);
|
|
1097
|
+
if (tierPowers.length === 0) continue;
|
|
1098
|
+
process.stdout.write("\n");
|
|
1099
|
+
process.stdout.write(` ${color.cyan(TIER_LABELS[tier])}:
|
|
1100
|
+
`);
|
|
1101
|
+
for (const power of tierPowers) {
|
|
1102
|
+
process.stdout.write(` ${color.bold(power.name)} - ${power.description}
|
|
1103
|
+
`);
|
|
1104
|
+
process.stdout.write(` ${color.dim(power.url)}
|
|
1105
|
+
`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
const counts = countByTier(powers);
|
|
1109
|
+
const parts = [];
|
|
1110
|
+
if (counts.essential > 0) parts.push(`${counts.essential} essential`);
|
|
1111
|
+
if (counts.recommended > 0) parts.push(`${counts.recommended} recommended`);
|
|
1112
|
+
if (counts.optional > 0) parts.push(`${counts.optional} optional`);
|
|
1113
|
+
process.stdout.write("\n");
|
|
1114
|
+
process.stdout.write(color.dim(`${parts.join(", ")} Powers recommended`) + "\n");
|
|
1115
|
+
}
|
|
1116
|
+
function countByTier(powers) {
|
|
1117
|
+
const counts = { essential: 0, recommended: 0, optional: 0 };
|
|
1118
|
+
for (const p of powers) {
|
|
1119
|
+
counts[p.tier]++;
|
|
1120
|
+
}
|
|
1121
|
+
return counts;
|
|
1122
|
+
}
|
|
1123
|
+
async function interactiveTierPrompt(powers) {
|
|
1124
|
+
if (!process.stdin.isTTY) {
|
|
1125
|
+
return { selectedTiers: ["essential", "recommended"], confirmMCP: true };
|
|
1126
|
+
}
|
|
1127
|
+
return new Promise((resolve2, reject) => {
|
|
1128
|
+
let cursor = 1;
|
|
1129
|
+
let rendered = false;
|
|
1130
|
+
const rl = readline.createInterface({
|
|
1131
|
+
input: process.stdin,
|
|
1132
|
+
output: process.stdout,
|
|
1133
|
+
terminal: false
|
|
1134
|
+
});
|
|
1135
|
+
const render = () => {
|
|
1136
|
+
if (rendered) {
|
|
1137
|
+
process.stdout.write("\x1B[u");
|
|
1138
|
+
} else {
|
|
1139
|
+
process.stdout.write("\x1B[s");
|
|
1140
|
+
}
|
|
1141
|
+
rendered = true;
|
|
1142
|
+
process.stdout.write("\x1B[J");
|
|
1143
|
+
process.stdout.write(
|
|
1144
|
+
color.bold("? Which Powers tiers to include in setup guide?") + color.dim(" (Arrow keys to select, Enter to confirm)") + "\n"
|
|
1145
|
+
);
|
|
1146
|
+
for (let i = 0; i < TIER_OPTIONS.length; i++) {
|
|
1147
|
+
const marker = cursor === i ? color.cyan(">") : " ";
|
|
1148
|
+
const label = cursor === i ? color.cyan(TIER_OPTIONS[i].label) : TIER_OPTIONS[i].label;
|
|
1149
|
+
process.stdout.write(` ${marker} ${label}
|
|
1150
|
+
`);
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
render();
|
|
1154
|
+
process.stdin.setRawMode(true);
|
|
1155
|
+
process.stdin.resume();
|
|
1156
|
+
process.stdin.setEncoding("utf-8");
|
|
1157
|
+
let escBuffer = "";
|
|
1158
|
+
const handleArrow = (seq) => {
|
|
1159
|
+
if (seq === "\x1B[A" || seq === "\x1BOA") {
|
|
1160
|
+
cursor = (cursor - 1 + TIER_OPTIONS.length) % TIER_OPTIONS.length;
|
|
1161
|
+
render();
|
|
1162
|
+
return true;
|
|
1163
|
+
}
|
|
1164
|
+
if (seq === "\x1B[B" || seq === "\x1BOB") {
|
|
1165
|
+
cursor = (cursor + 1) % TIER_OPTIONS.length;
|
|
1166
|
+
render();
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
return false;
|
|
1170
|
+
};
|
|
1171
|
+
const cleanup = () => {
|
|
1172
|
+
process.stdin.setRawMode(false);
|
|
1173
|
+
process.stdin.removeListener("data", onData);
|
|
1174
|
+
process.stdin.pause();
|
|
1175
|
+
rl.close();
|
|
1176
|
+
};
|
|
1177
|
+
const onData = (key) => {
|
|
1178
|
+
if (escBuffer.length > 0) {
|
|
1179
|
+
escBuffer += key;
|
|
1180
|
+
if (escBuffer.length >= 3) {
|
|
1181
|
+
const seq = escBuffer;
|
|
1182
|
+
escBuffer = "";
|
|
1183
|
+
handleArrow(seq);
|
|
1184
|
+
}
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
if (key.length >= 3 && key.startsWith("\x1B")) {
|
|
1188
|
+
handleArrow(key);
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
if (key === "\x1B") {
|
|
1192
|
+
escBuffer = key;
|
|
1193
|
+
setTimeout(() => {
|
|
1194
|
+
if (escBuffer.length > 0) {
|
|
1195
|
+
escBuffer = "";
|
|
1196
|
+
}
|
|
1197
|
+
}, 50);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (key === "") {
|
|
1201
|
+
cleanup();
|
|
1202
|
+
reject(new Error("SIGINT"));
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
if (key === "\r" || key === "\n") {
|
|
1206
|
+
cleanup();
|
|
1207
|
+
const selected = TIER_OPTIONS[cursor];
|
|
1208
|
+
const confirmMCP = selected.tiers.length > 0;
|
|
1209
|
+
resolve2({ selectedTiers: selected.tiers, confirmMCP });
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
if (key === "k") {
|
|
1213
|
+
cursor = (cursor - 1 + TIER_OPTIONS.length) % TIER_OPTIONS.length;
|
|
1214
|
+
render();
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
if (key === "j") {
|
|
1218
|
+
cursor = (cursor + 1) % TIER_OPTIONS.length;
|
|
1219
|
+
render();
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
process.stdin.on("data", onData);
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
668
1227
|
// src/commands/init.ts
|
|
669
1228
|
function setupSigintHandler() {
|
|
670
1229
|
process5.on("SIGINT", () => {
|
|
@@ -674,7 +1233,7 @@ function setupSigintHandler() {
|
|
|
674
1233
|
async function multiPickPrompt(items) {
|
|
675
1234
|
const selected = /* @__PURE__ */ new Set();
|
|
676
1235
|
let cursor = 0;
|
|
677
|
-
const rl =
|
|
1236
|
+
const rl = readline2.createInterface({
|
|
678
1237
|
input: process5.stdin,
|
|
679
1238
|
output: process5.stdout,
|
|
680
1239
|
terminal: false
|
|
@@ -687,18 +1246,21 @@ async function multiPickPrompt(items) {
|
|
|
687
1246
|
let rendered = false;
|
|
688
1247
|
const render = () => {
|
|
689
1248
|
if (rendered) {
|
|
690
|
-
process5.stdout.write(
|
|
1249
|
+
process5.stdout.write("\x1B[u");
|
|
1250
|
+
} else {
|
|
1251
|
+
process5.stdout.write("\x1B[s");
|
|
691
1252
|
}
|
|
692
1253
|
rendered = true;
|
|
1254
|
+
process5.stdout.write("\x1B[J");
|
|
693
1255
|
process5.stdout.write(
|
|
694
|
-
|
|
1256
|
+
color.bold("? Select presets to install:") + color.dim(" (Space to select, <a> toggle all, Enter to confirm)") + "\n"
|
|
695
1257
|
);
|
|
696
1258
|
for (let i = 0; i < items.length; i++) {
|
|
697
1259
|
const marker = cursor === i ? color.cyan(">") : " ";
|
|
698
1260
|
const check = selected.has(i) ? color.green("[x]") : "[ ]";
|
|
699
1261
|
const name = color.bold(items[i].name.padEnd(12));
|
|
700
1262
|
const desc = color.dim(`- ${items[i].description}`);
|
|
701
|
-
process5.stdout.write(
|
|
1263
|
+
process5.stdout.write(` ${marker} ${check} ${name} ${desc}
|
|
702
1264
|
`);
|
|
703
1265
|
}
|
|
704
1266
|
};
|
|
@@ -795,7 +1357,7 @@ async function multiPickPrompt(items) {
|
|
|
795
1357
|
async function confirmPrompt(message) {
|
|
796
1358
|
if (!process5.stdin.isTTY) return true;
|
|
797
1359
|
return new Promise((resolve2, reject) => {
|
|
798
|
-
const rl =
|
|
1360
|
+
const rl = readline2.createInterface({
|
|
799
1361
|
input: process5.stdin,
|
|
800
1362
|
output: process5.stdout
|
|
801
1363
|
});
|
|
@@ -812,7 +1374,7 @@ async function confirmPrompt(message) {
|
|
|
812
1374
|
}
|
|
813
1375
|
async function conflictPrompt(target) {
|
|
814
1376
|
if (!process5.stdin.isTTY) return "skip";
|
|
815
|
-
const relTarget =
|
|
1377
|
+
const relTarget = path13.relative(process5.cwd(), target);
|
|
816
1378
|
process5.stdout.write(
|
|
817
1379
|
`
|
|
818
1380
|
${color.yellow("?")} File ${color.bold(relTarget)} already exists with different content.
|
|
@@ -823,7 +1385,7 @@ ${color.yellow("?")} File ${color.bold(relTarget)} already exists with different
|
|
|
823
1385
|
`
|
|
824
1386
|
);
|
|
825
1387
|
return new Promise((resolve2, reject) => {
|
|
826
|
-
const rl =
|
|
1388
|
+
const rl = readline2.createInterface({
|
|
827
1389
|
input: process5.stdin,
|
|
828
1390
|
output: process5.stdout
|
|
829
1391
|
});
|
|
@@ -846,7 +1408,7 @@ function sha2562(data) {
|
|
|
846
1408
|
return crypto3.createHash("sha256").update(data).digest("hex");
|
|
847
1409
|
}
|
|
848
1410
|
function registerInitCommand(program2) {
|
|
849
|
-
program2.command("init").description("Initialize workspace with selected presets").option("-y, --yes", "Skip confirmation, accept defaults").option("--preset <name>", "Specify preset (repeatable)", collectPreset, []).option("--force", "Overwrite all files (with backup)").option("--skip-existing", "Skip all existing files").option("--no-color", "Disable ANSI colors").action(async (opts) => {
|
|
1411
|
+
program2.command("init").description("Initialize workspace with selected presets").option("-y, --yes", "Skip confirmation, accept defaults").option("--preset <name>", "Specify preset (repeatable)", collectPreset, []).option("--force", "Overwrite all files (with backup)").option("--skip-existing", "Skip all existing files").option("--no-color", "Disable ANSI colors").option("--powers <mode>", "Powers setup mode: none, all, or interactive (default)", "interactive").option("--quiet", "Suppress non-essential output including Powers recommendations").action(async (opts) => {
|
|
850
1412
|
setupSigintHandler();
|
|
851
1413
|
try {
|
|
852
1414
|
await runInit(opts);
|
|
@@ -923,18 +1485,18 @@ async function runInit(opts) {
|
|
|
923
1485
|
const settingsFiles = manifest.files.filter((f) => f.type === "settings");
|
|
924
1486
|
const statuslineFiles = manifest.files.filter((f) => f.type === "statusline");
|
|
925
1487
|
for (const fileEntry of regularFiles) {
|
|
926
|
-
const sourcePath =
|
|
927
|
-
const targetPath =
|
|
1488
|
+
const sourcePath = path13.join(presetDir, fileEntry.source);
|
|
1489
|
+
const targetPath = path13.resolve(workspaceRoot, fileEntry.target);
|
|
928
1490
|
if (!safePathInside(workspaceRoot, fileEntry.target)) {
|
|
929
1491
|
logger.warn(`Skipping unsafe path: ${fileEntry.target}`);
|
|
930
1492
|
filesSkipped++;
|
|
931
1493
|
continue;
|
|
932
1494
|
}
|
|
933
|
-
if (!
|
|
1495
|
+
if (!fs14.existsSync(sourcePath)) {
|
|
934
1496
|
logger.debug(`Source file missing: ${sourcePath}`);
|
|
935
1497
|
continue;
|
|
936
1498
|
}
|
|
937
|
-
const sourceContent =
|
|
1499
|
+
const sourceContent = fs14.readFileSync(sourcePath);
|
|
938
1500
|
const action = await resolve({
|
|
939
1501
|
target: targetPath,
|
|
940
1502
|
sourceContent,
|
|
@@ -945,11 +1507,11 @@ async function runInit(opts) {
|
|
|
945
1507
|
});
|
|
946
1508
|
switch (action) {
|
|
947
1509
|
case "WRITE_NEW":
|
|
948
|
-
|
|
1510
|
+
fs14.mkdirSync(path13.dirname(targetPath), { recursive: true });
|
|
949
1511
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
950
1512
|
if (fileEntry.executable && process5.platform !== "win32") {
|
|
951
1513
|
try {
|
|
952
|
-
|
|
1514
|
+
fs14.chmodSync(targetPath, 493);
|
|
953
1515
|
} catch {
|
|
954
1516
|
}
|
|
955
1517
|
}
|
|
@@ -960,7 +1522,7 @@ async function runInit(opts) {
|
|
|
960
1522
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
961
1523
|
if (fileEntry.executable && process5.platform !== "win32") {
|
|
962
1524
|
try {
|
|
963
|
-
|
|
1525
|
+
fs14.chmodSync(targetPath, 493);
|
|
964
1526
|
} catch {
|
|
965
1527
|
}
|
|
966
1528
|
}
|
|
@@ -994,54 +1556,147 @@ async function runInit(opts) {
|
|
|
994
1556
|
}
|
|
995
1557
|
}
|
|
996
1558
|
if (mcpFiles.length > 0 && manifest.mcpServers) {
|
|
997
|
-
const mcpPath =
|
|
1559
|
+
const mcpPath = path13.join(workspaceRoot, ".kiro/settings/mcp.json");
|
|
998
1560
|
let existingMcp = null;
|
|
999
|
-
if (
|
|
1561
|
+
if (fs14.existsSync(mcpPath)) {
|
|
1000
1562
|
try {
|
|
1001
|
-
existingMcp = JSON.parse(
|
|
1563
|
+
existingMcp = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
|
|
1002
1564
|
} catch {
|
|
1003
1565
|
existingMcp = null;
|
|
1004
1566
|
}
|
|
1005
1567
|
}
|
|
1006
1568
|
const merged = mergeMCP(existingMcp, manifest.mcpServers, manifest.name);
|
|
1007
|
-
|
|
1569
|
+
fs14.mkdirSync(path13.dirname(mcpPath), { recursive: true });
|
|
1008
1570
|
atomicWrite(mcpPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1009
1571
|
filesWritten++;
|
|
1010
1572
|
}
|
|
1011
1573
|
if (settingsFiles.length > 0) {
|
|
1012
|
-
const settingsPath =
|
|
1574
|
+
const settingsPath = path13.join(workspaceRoot, ".kiro/settings.json");
|
|
1013
1575
|
let existingSettings = null;
|
|
1014
|
-
if (
|
|
1576
|
+
if (fs14.existsSync(settingsPath)) {
|
|
1015
1577
|
try {
|
|
1016
1578
|
existingSettings = JSON.parse(
|
|
1017
|
-
|
|
1579
|
+
fs14.readFileSync(settingsPath, "utf-8")
|
|
1018
1580
|
);
|
|
1019
1581
|
} catch {
|
|
1020
1582
|
existingSettings = null;
|
|
1021
1583
|
}
|
|
1022
1584
|
}
|
|
1023
|
-
const presetSettingsPath =
|
|
1024
|
-
if (
|
|
1585
|
+
const presetSettingsPath = path13.join(presetDir, "settings.json");
|
|
1586
|
+
if (fs14.existsSync(presetSettingsPath)) {
|
|
1025
1587
|
const presetSettings = JSON.parse(
|
|
1026
|
-
|
|
1588
|
+
fs14.readFileSync(presetSettingsPath, "utf-8")
|
|
1027
1589
|
);
|
|
1028
1590
|
const resolvedSettings = resolveSettingsCommand(
|
|
1029
1591
|
presetSettings
|
|
1030
1592
|
);
|
|
1031
1593
|
const merged = mergeSettings(existingSettings, resolvedSettings);
|
|
1032
|
-
|
|
1594
|
+
fs14.mkdirSync(path13.dirname(settingsPath), { recursive: true });
|
|
1033
1595
|
atomicWrite(settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1034
1596
|
filesWritten++;
|
|
1035
1597
|
}
|
|
1036
1598
|
}
|
|
1037
|
-
const mcpExampleSource =
|
|
1038
|
-
if (
|
|
1039
|
-
const mcpExampleTarget =
|
|
1040
|
-
if (!
|
|
1041
|
-
|
|
1042
|
-
|
|
1599
|
+
const mcpExampleSource = path13.join(presetDir, ".mcp.json.example");
|
|
1600
|
+
if (fs14.existsSync(mcpExampleSource)) {
|
|
1601
|
+
const mcpExampleTarget = path13.join(workspaceRoot, ".kiro/.mcp.json.example");
|
|
1602
|
+
if (!fs14.existsSync(mcpExampleTarget)) {
|
|
1603
|
+
fs14.mkdirSync(path13.dirname(mcpExampleTarget), { recursive: true });
|
|
1604
|
+
fs14.copyFileSync(mcpExampleSource, mcpExampleTarget);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
try {
|
|
1609
|
+
if (opts.powers !== "none") {
|
|
1610
|
+
const allPowerEntries = presets.map((p) => {
|
|
1611
|
+
const result = loadPowers(p.dir);
|
|
1612
|
+
if (!result.ok) {
|
|
1613
|
+
logger.warn(`Powers: ${result.error}`);
|
|
1614
|
+
}
|
|
1615
|
+
return result.powers;
|
|
1616
|
+
});
|
|
1617
|
+
const mergedPowers = mergePowers(allPowerEntries);
|
|
1618
|
+
if (mergedPowers.length > 0) {
|
|
1619
|
+
const promptResult = await promptPowersTier(mergedPowers, {
|
|
1620
|
+
powersFlag: opts.powers,
|
|
1621
|
+
yes: opts.yes
|
|
1622
|
+
});
|
|
1623
|
+
const filteredPowers = promptResult.selectedTiers.length > 0 ? filterByTier(mergedPowers, promptResult.selectedTiers) : [];
|
|
1624
|
+
if (promptResult.confirmMCP) {
|
|
1625
|
+
try {
|
|
1626
|
+
const presetMCPConfigs = presets.map((p) => getMCPConfig(p.manifest.name));
|
|
1627
|
+
let combinedMCP = null;
|
|
1628
|
+
const mcpJsonPath = path13.join(workspaceRoot, ".mcp.json");
|
|
1629
|
+
if (fs14.existsSync(mcpJsonPath)) {
|
|
1630
|
+
try {
|
|
1631
|
+
combinedMCP = JSON.parse(fs14.readFileSync(mcpJsonPath, "utf-8"));
|
|
1632
|
+
} catch {
|
|
1633
|
+
logger.warn("Existing .mcp.json is invalid JSON, will create fresh.");
|
|
1634
|
+
combinedMCP = null;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
for (const mcpConfig of presetMCPConfigs) {
|
|
1638
|
+
combinedMCP = mergeMCPConfig(combinedMCP, mcpConfig);
|
|
1639
|
+
}
|
|
1640
|
+
let confirmWrite = true;
|
|
1641
|
+
if (!opts.yes) {
|
|
1642
|
+
confirmWrite = await confirmPrompt(
|
|
1643
|
+
"Configure MCP servers in .mcp.json? (Y/n)"
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
if (confirmWrite && combinedMCP) {
|
|
1647
|
+
writeMCPConfig(workspaceRoot, combinedMCP);
|
|
1648
|
+
if (!opts.quiet) {
|
|
1649
|
+
logger.info("MCP servers configured in .mcp.json");
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
} catch (err) {
|
|
1653
|
+
logger.warn(`MCP configuration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
displayPowersRecommendations(filteredPowers, opts.quiet ?? false);
|
|
1657
|
+
try {
|
|
1658
|
+
const mcpServersForGuide = presets.reduce(
|
|
1659
|
+
(acc, p) => ({ ...acc, ...getMCPConfig(p.manifest.name).servers }),
|
|
1660
|
+
{}
|
|
1661
|
+
);
|
|
1662
|
+
const guideContent = generateSetupGuide({
|
|
1663
|
+
powers: filteredPowers,
|
|
1664
|
+
presetNames: selectedNames,
|
|
1665
|
+
mcpServers: mcpServersForGuide
|
|
1666
|
+
});
|
|
1667
|
+
writeSetupGuide(workspaceRoot, guideContent);
|
|
1668
|
+
if (!opts.quiet) {
|
|
1669
|
+
logger.info("Setup guide written to .kiro/POWERS-SETUP.md");
|
|
1670
|
+
}
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
logger.warn(`Setup guide generation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1673
|
+
}
|
|
1674
|
+
try {
|
|
1675
|
+
const mcpConfigForEnv = presets.reduce(
|
|
1676
|
+
(acc, p) => {
|
|
1677
|
+
const cfg = getMCPConfig(p.manifest.name);
|
|
1678
|
+
return { servers: { ...acc.servers, ...cfg.servers } };
|
|
1679
|
+
},
|
|
1680
|
+
{ servers: {} }
|
|
1681
|
+
);
|
|
1682
|
+
const envVars = collectEnvVars(mcpConfigForEnv, filteredPowers);
|
|
1683
|
+
if (envVars.length > 0) {
|
|
1684
|
+
const existingEnv = readExistingEnv(workspaceRoot);
|
|
1685
|
+
const envContent = generateEnvTemplate(existingEnv, envVars);
|
|
1686
|
+
if (envContent.trim().length > 0) {
|
|
1687
|
+
writeEnvTemplate(workspaceRoot, envContent);
|
|
1688
|
+
if (!opts.quiet) {
|
|
1689
|
+
logger.info("Environment template updated in .env.example");
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
} catch (err) {
|
|
1694
|
+
logger.warn(`Env template generation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1695
|
+
}
|
|
1043
1696
|
}
|
|
1044
1697
|
}
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
logger.warn(`Powers integration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1045
1700
|
}
|
|
1046
1701
|
const kitVersion = getKitVersion();
|
|
1047
1702
|
const presetMetas = presets.map((p) => ({
|
|
@@ -1091,7 +1746,7 @@ function generateTimestamp2() {
|
|
|
1091
1746
|
function getKitVersion() {
|
|
1092
1747
|
try {
|
|
1093
1748
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
1094
|
-
const pkg2 = JSON.parse(
|
|
1749
|
+
const pkg2 = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
1095
1750
|
return pkg2.version;
|
|
1096
1751
|
} catch {
|
|
1097
1752
|
return "0.1.0";
|
|
@@ -1099,8 +1754,8 @@ function getKitVersion() {
|
|
|
1099
1754
|
}
|
|
1100
1755
|
|
|
1101
1756
|
// src/commands/add.ts
|
|
1102
|
-
import
|
|
1103
|
-
import
|
|
1757
|
+
import fs15 from "fs";
|
|
1758
|
+
import path14 from "path";
|
|
1104
1759
|
import process6 from "process";
|
|
1105
1760
|
import crypto4 from "crypto";
|
|
1106
1761
|
function sha2563(data) {
|
|
@@ -1120,7 +1775,7 @@ function generateTimestamp3() {
|
|
|
1120
1775
|
function getKitVersion2() {
|
|
1121
1776
|
try {
|
|
1122
1777
|
const pkgPath = new URL("../../package.json", import.meta.url);
|
|
1123
|
-
const pkg2 = JSON.parse(
|
|
1778
|
+
const pkg2 = JSON.parse(fs15.readFileSync(pkgPath, "utf-8"));
|
|
1124
1779
|
return pkg2.version;
|
|
1125
1780
|
} catch {
|
|
1126
1781
|
return "0.1.0";
|
|
@@ -1128,8 +1783,8 @@ function getKitVersion2() {
|
|
|
1128
1783
|
}
|
|
1129
1784
|
async function conflictPrompt2(target) {
|
|
1130
1785
|
if (!process6.stdin.isTTY) return "skip";
|
|
1131
|
-
const { default:
|
|
1132
|
-
const relTarget =
|
|
1786
|
+
const { default: readline3 } = await import("readline");
|
|
1787
|
+
const relTarget = path14.relative(process6.cwd(), target);
|
|
1133
1788
|
process6.stdout.write(
|
|
1134
1789
|
`
|
|
1135
1790
|
${color.yellow("?")} File ${color.bold(relTarget)} already exists with different content.
|
|
@@ -1140,7 +1795,7 @@ ${color.yellow("?")} File ${color.bold(relTarget)} already exists with different
|
|
|
1140
1795
|
`
|
|
1141
1796
|
);
|
|
1142
1797
|
return new Promise((resolve2) => {
|
|
1143
|
-
const rl =
|
|
1798
|
+
const rl = readline3.createInterface({ input: process6.stdin, output: process6.stdout });
|
|
1144
1799
|
rl.question(` Choice (overwrite/skip/diff/all): `, (answer) => {
|
|
1145
1800
|
rl.close();
|
|
1146
1801
|
const a = answer.trim().toLowerCase();
|
|
@@ -1171,9 +1826,9 @@ async function runAdd(presetName, opts) {
|
|
|
1171
1826
|
);
|
|
1172
1827
|
process6.exit(1);
|
|
1173
1828
|
}
|
|
1174
|
-
const kiroDir =
|
|
1175
|
-
if (!
|
|
1176
|
-
|
|
1829
|
+
const kiroDir = path14.join(workspaceRoot, ".kiro");
|
|
1830
|
+
if (!fs15.existsSync(kiroDir)) {
|
|
1831
|
+
fs15.mkdirSync(kiroDir, { recursive: true });
|
|
1177
1832
|
logger.info("Created .kiro/ directory.");
|
|
1178
1833
|
}
|
|
1179
1834
|
const preset = load(presetName);
|
|
@@ -1192,18 +1847,18 @@ async function runAdd(presetName, opts) {
|
|
|
1192
1847
|
);
|
|
1193
1848
|
const statuslineFiles = manifest.files.filter((f) => f.type === "statusline");
|
|
1194
1849
|
for (const fileEntry of regularFiles) {
|
|
1195
|
-
const sourcePath =
|
|
1196
|
-
const targetPath =
|
|
1850
|
+
const sourcePath = path14.join(presetDir, fileEntry.source);
|
|
1851
|
+
const targetPath = path14.resolve(workspaceRoot, fileEntry.target);
|
|
1197
1852
|
if (!safePathInside(workspaceRoot, fileEntry.target)) {
|
|
1198
1853
|
logger.warn(`Skipping unsafe path: ${fileEntry.target}`);
|
|
1199
1854
|
filesSkipped++;
|
|
1200
1855
|
continue;
|
|
1201
1856
|
}
|
|
1202
|
-
if (!
|
|
1857
|
+
if (!fs15.existsSync(sourcePath)) {
|
|
1203
1858
|
logger.debug(`Source file missing: ${sourcePath}`);
|
|
1204
1859
|
continue;
|
|
1205
1860
|
}
|
|
1206
|
-
const sourceContent =
|
|
1861
|
+
const sourceContent = fs15.readFileSync(sourcePath);
|
|
1207
1862
|
const action = await resolve({
|
|
1208
1863
|
target: targetPath,
|
|
1209
1864
|
sourceContent,
|
|
@@ -1214,11 +1869,11 @@ async function runAdd(presetName, opts) {
|
|
|
1214
1869
|
});
|
|
1215
1870
|
switch (action) {
|
|
1216
1871
|
case "WRITE_NEW":
|
|
1217
|
-
|
|
1872
|
+
fs15.mkdirSync(path14.dirname(targetPath), { recursive: true });
|
|
1218
1873
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
1219
1874
|
if (fileEntry.executable && process6.platform !== "win32") {
|
|
1220
1875
|
try {
|
|
1221
|
-
|
|
1876
|
+
fs15.chmodSync(targetPath, 493);
|
|
1222
1877
|
} catch {
|
|
1223
1878
|
}
|
|
1224
1879
|
}
|
|
@@ -1229,7 +1884,7 @@ async function runAdd(presetName, opts) {
|
|
|
1229
1884
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
1230
1885
|
if (fileEntry.executable && process6.platform !== "win32") {
|
|
1231
1886
|
try {
|
|
1232
|
-
|
|
1887
|
+
fs15.chmodSync(targetPath, 493);
|
|
1233
1888
|
} catch {
|
|
1234
1889
|
}
|
|
1235
1890
|
}
|
|
@@ -1263,37 +1918,37 @@ async function runAdd(presetName, opts) {
|
|
|
1263
1918
|
}
|
|
1264
1919
|
}
|
|
1265
1920
|
if (manifest.mcpServers) {
|
|
1266
|
-
const mcpPath =
|
|
1921
|
+
const mcpPath = path14.join(workspaceRoot, ".kiro/settings/mcp.json");
|
|
1267
1922
|
let existingMcp = null;
|
|
1268
|
-
if (
|
|
1923
|
+
if (fs15.existsSync(mcpPath)) {
|
|
1269
1924
|
try {
|
|
1270
|
-
existingMcp = JSON.parse(
|
|
1925
|
+
existingMcp = JSON.parse(fs15.readFileSync(mcpPath, "utf-8"));
|
|
1271
1926
|
} catch {
|
|
1272
1927
|
existingMcp = null;
|
|
1273
1928
|
}
|
|
1274
1929
|
}
|
|
1275
1930
|
const merged = mergeMCP(existingMcp, manifest.mcpServers, manifest.name);
|
|
1276
|
-
|
|
1931
|
+
fs15.mkdirSync(path14.dirname(mcpPath), { recursive: true });
|
|
1277
1932
|
atomicWrite(mcpPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1278
1933
|
filesWritten++;
|
|
1279
1934
|
}
|
|
1280
|
-
const presetSettingsPath =
|
|
1281
|
-
if (
|
|
1282
|
-
const settingsPath =
|
|
1935
|
+
const presetSettingsPath = path14.join(presetDir, "settings.json");
|
|
1936
|
+
if (fs15.existsSync(presetSettingsPath)) {
|
|
1937
|
+
const settingsPath = path14.join(workspaceRoot, ".kiro/settings.json");
|
|
1283
1938
|
let existingSettings = null;
|
|
1284
|
-
if (
|
|
1939
|
+
if (fs15.existsSync(settingsPath)) {
|
|
1285
1940
|
try {
|
|
1286
|
-
existingSettings = JSON.parse(
|
|
1941
|
+
existingSettings = JSON.parse(fs15.readFileSync(settingsPath, "utf-8"));
|
|
1287
1942
|
} catch {
|
|
1288
1943
|
existingSettings = null;
|
|
1289
1944
|
}
|
|
1290
1945
|
}
|
|
1291
|
-
const presetSettings = JSON.parse(
|
|
1946
|
+
const presetSettings = JSON.parse(fs15.readFileSync(presetSettingsPath, "utf-8"));
|
|
1292
1947
|
const resolvedSettings = resolveSettingsCommand(
|
|
1293
1948
|
presetSettings
|
|
1294
1949
|
);
|
|
1295
1950
|
const merged = mergeSettings(existingSettings, resolvedSettings);
|
|
1296
|
-
|
|
1951
|
+
fs15.mkdirSync(path14.dirname(settingsPath), { recursive: true });
|
|
1297
1952
|
atomicWrite(settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
1298
1953
|
filesWritten++;
|
|
1299
1954
|
}
|
|
@@ -1508,8 +2163,8 @@ ${color.bold(manifest.name)} v${manifest.version}
|
|
|
1508
2163
|
}
|
|
1509
2164
|
|
|
1510
2165
|
// src/commands/update.ts
|
|
1511
|
-
import
|
|
1512
|
-
import
|
|
2166
|
+
import fs16 from "fs";
|
|
2167
|
+
import path15 from "path";
|
|
1513
2168
|
import process9 from "process";
|
|
1514
2169
|
import crypto5 from "crypto";
|
|
1515
2170
|
function sha2564(data) {
|
|
@@ -1529,7 +2184,7 @@ function generateTimestamp4() {
|
|
|
1529
2184
|
function getKitVersion3() {
|
|
1530
2185
|
try {
|
|
1531
2186
|
const pkgPath = new URL("../../package.json", import.meta.url);
|
|
1532
|
-
const pkg2 = JSON.parse(
|
|
2187
|
+
const pkg2 = JSON.parse(fs16.readFileSync(pkgPath, "utf-8"));
|
|
1533
2188
|
return pkg2.version;
|
|
1534
2189
|
} catch {
|
|
1535
2190
|
return "0.1.0";
|
|
@@ -1537,8 +2192,8 @@ function getKitVersion3() {
|
|
|
1537
2192
|
}
|
|
1538
2193
|
async function conflictPrompt3(target) {
|
|
1539
2194
|
if (!process9.stdin.isTTY) return "skip";
|
|
1540
|
-
const { default:
|
|
1541
|
-
const relTarget =
|
|
2195
|
+
const { default: readline3 } = await import("readline");
|
|
2196
|
+
const relTarget = path15.relative(process9.cwd(), target);
|
|
1542
2197
|
process9.stdout.write(
|
|
1543
2198
|
`
|
|
1544
2199
|
${color.yellow("?")} File ${color.bold(relTarget)} has changed in the new version.
|
|
@@ -1549,7 +2204,7 @@ ${color.yellow("?")} File ${color.bold(relTarget)} has changed in the new versio
|
|
|
1549
2204
|
`
|
|
1550
2205
|
);
|
|
1551
2206
|
return new Promise((resolve2) => {
|
|
1552
|
-
const rl =
|
|
2207
|
+
const rl = readline3.createInterface({ input: process9.stdin, output: process9.stdout });
|
|
1553
2208
|
rl.question(` Choice (overwrite/skip/diff/all): `, (answer) => {
|
|
1554
2209
|
rl.close();
|
|
1555
2210
|
const a = answer.trim().toLowerCase();
|
|
@@ -1606,11 +2261,11 @@ async function runUpdate(opts) {
|
|
|
1606
2261
|
const updatedFiles = [];
|
|
1607
2262
|
for (const fileEntry of bundled.manifest.files) {
|
|
1608
2263
|
if (["mcp", "settings", "statusline"].includes(fileEntry.type)) continue;
|
|
1609
|
-
const sourcePath =
|
|
1610
|
-
const targetPath =
|
|
2264
|
+
const sourcePath = path15.join(presetDir, fileEntry.source);
|
|
2265
|
+
const targetPath = path15.resolve(workspaceRoot, fileEntry.target);
|
|
1611
2266
|
if (!safePathInside(workspaceRoot, fileEntry.target)) continue;
|
|
1612
|
-
if (!
|
|
1613
|
-
const sourceContent =
|
|
2267
|
+
if (!fs16.existsSync(sourcePath)) continue;
|
|
2268
|
+
const sourceContent = fs16.readFileSync(sourcePath);
|
|
1614
2269
|
const newHash = sha2564(sourceContent);
|
|
1615
2270
|
const trackedFile = trackedPreset.files.find((f) => f.target === fileEntry.target);
|
|
1616
2271
|
if (trackedFile && trackedFile.contentHash === newHash) {
|
|
@@ -1626,11 +2281,11 @@ async function runUpdate(opts) {
|
|
|
1626
2281
|
});
|
|
1627
2282
|
switch (action) {
|
|
1628
2283
|
case "WRITE_NEW":
|
|
1629
|
-
|
|
2284
|
+
fs16.mkdirSync(path15.dirname(targetPath), { recursive: true });
|
|
1630
2285
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
1631
2286
|
if (fileEntry.executable && process9.platform !== "win32") {
|
|
1632
2287
|
try {
|
|
1633
|
-
|
|
2288
|
+
fs16.chmodSync(targetPath, 493);
|
|
1634
2289
|
} catch {
|
|
1635
2290
|
}
|
|
1636
2291
|
}
|
|
@@ -1641,7 +2296,7 @@ async function runUpdate(opts) {
|
|
|
1641
2296
|
atomicWrite(targetPath, sourceContent.toString("utf-8"));
|
|
1642
2297
|
if (fileEntry.executable && process9.platform !== "win32") {
|
|
1643
2298
|
try {
|
|
1644
|
-
|
|
2299
|
+
fs16.chmodSync(targetPath, 493);
|
|
1645
2300
|
} catch {
|
|
1646
2301
|
}
|
|
1647
2302
|
}
|
|
@@ -1729,8 +2384,8 @@ function runRestore(opts) {
|
|
|
1729
2384
|
}
|
|
1730
2385
|
|
|
1731
2386
|
// src/commands/doctor.ts
|
|
1732
|
-
import
|
|
1733
|
-
import
|
|
2387
|
+
import fs17 from "fs";
|
|
2388
|
+
import path16 from "path";
|
|
1734
2389
|
import process11 from "process";
|
|
1735
2390
|
function registerDoctorCommand(program2) {
|
|
1736
2391
|
program2.command("doctor").description("Run workspace health checks").option("--fix", "Auto-fix fixable issues").action((opts) => {
|
|
@@ -1793,8 +2448,8 @@ function checkNodeVersion() {
|
|
|
1793
2448
|
};
|
|
1794
2449
|
}
|
|
1795
2450
|
function checkKiroDir(workspaceRoot) {
|
|
1796
|
-
const kiroDir =
|
|
1797
|
-
if (
|
|
2451
|
+
const kiroDir = path16.join(workspaceRoot, ".kiro");
|
|
2452
|
+
if (fs17.existsSync(kiroDir) && fs17.statSync(kiroDir).isDirectory()) {
|
|
1798
2453
|
return { name: "kiro-dir", result: "PASS", message: ".kiro/ directory exists" };
|
|
1799
2454
|
}
|
|
1800
2455
|
return {
|
|
@@ -1804,12 +2459,12 @@ function checkKiroDir(workspaceRoot) {
|
|
|
1804
2459
|
};
|
|
1805
2460
|
}
|
|
1806
2461
|
function checkMcpJson(workspaceRoot) {
|
|
1807
|
-
const mcpPath =
|
|
1808
|
-
if (!
|
|
2462
|
+
const mcpPath = path16.join(workspaceRoot, ".kiro/settings/mcp.json");
|
|
2463
|
+
if (!fs17.existsSync(mcpPath)) {
|
|
1809
2464
|
return { name: "mcp-json", result: "WARN", message: ".kiro/settings/mcp.json not found (optional)" };
|
|
1810
2465
|
}
|
|
1811
2466
|
try {
|
|
1812
|
-
const content =
|
|
2467
|
+
const content = fs17.readFileSync(mcpPath, "utf-8");
|
|
1813
2468
|
JSON.parse(content);
|
|
1814
2469
|
return { name: "mcp-json", result: "PASS", message: ".kiro/settings/mcp.json is valid JSON" };
|
|
1815
2470
|
} catch {
|
|
@@ -1819,19 +2474,19 @@ function checkMcpJson(workspaceRoot) {
|
|
|
1819
2474
|
message: ".kiro/settings/mcp.json is not valid JSON",
|
|
1820
2475
|
fixable: true,
|
|
1821
2476
|
fixAction: () => {
|
|
1822
|
-
const mcpPath2 =
|
|
1823
|
-
|
|
2477
|
+
const mcpPath2 = path16.join(workspaceRoot, ".kiro/settings/mcp.json");
|
|
2478
|
+
fs17.writeFileSync(mcpPath2, '{\n "mcpServers": {}\n}\n', "utf-8");
|
|
1824
2479
|
}
|
|
1825
2480
|
};
|
|
1826
2481
|
}
|
|
1827
2482
|
}
|
|
1828
2483
|
function checkTracking(workspaceRoot) {
|
|
1829
|
-
const trackingPath =
|
|
1830
|
-
if (!
|
|
2484
|
+
const trackingPath = path16.join(workspaceRoot, ".kiro/.kiro-kit.json");
|
|
2485
|
+
if (!fs17.existsSync(trackingPath)) {
|
|
1831
2486
|
return { name: "tracking", result: "WARN", message: ".kiro/.kiro-kit.json not found (no presets installed)" };
|
|
1832
2487
|
}
|
|
1833
2488
|
try {
|
|
1834
|
-
const content =
|
|
2489
|
+
const content = fs17.readFileSync(trackingPath, "utf-8");
|
|
1835
2490
|
JSON.parse(content);
|
|
1836
2491
|
return { name: "tracking", result: "PASS", message: ".kiro/.kiro-kit.json is valid" };
|
|
1837
2492
|
} catch {
|
|
@@ -1855,8 +2510,8 @@ function checkTrackedFiles(workspaceRoot) {
|
|
|
1855
2510
|
const missing = [];
|
|
1856
2511
|
for (const preset of tracking.presets) {
|
|
1857
2512
|
for (const file of preset.files) {
|
|
1858
|
-
const fullPath =
|
|
1859
|
-
if (!
|
|
2513
|
+
const fullPath = path16.resolve(workspaceRoot, file.target);
|
|
2514
|
+
if (!fs17.existsSync(fullPath)) {
|
|
1860
2515
|
missing.push(file.target);
|
|
1861
2516
|
}
|
|
1862
2517
|
}
|
|
@@ -1871,20 +2526,20 @@ function checkTrackedFiles(workspaceRoot) {
|
|
|
1871
2526
|
};
|
|
1872
2527
|
}
|
|
1873
2528
|
function checkSteeringFrontMatter(workspaceRoot) {
|
|
1874
|
-
const steeringDir =
|
|
1875
|
-
if (!
|
|
2529
|
+
const steeringDir = path16.join(workspaceRoot, ".kiro/steering");
|
|
2530
|
+
if (!fs17.existsSync(steeringDir)) {
|
|
1876
2531
|
return { name: "steering-fm", result: "PASS", message: "No steering files to check" };
|
|
1877
2532
|
}
|
|
1878
2533
|
const issues = [];
|
|
1879
2534
|
const walkDir = (dir) => {
|
|
1880
2535
|
let entries;
|
|
1881
2536
|
try {
|
|
1882
|
-
entries =
|
|
2537
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
1883
2538
|
} catch {
|
|
1884
2539
|
return;
|
|
1885
2540
|
}
|
|
1886
2541
|
for (const entry of entries) {
|
|
1887
|
-
const fullPath =
|
|
2542
|
+
const fullPath = path16.join(dir, entry.name);
|
|
1888
2543
|
if (entry.isDirectory()) {
|
|
1889
2544
|
walkDir(fullPath);
|
|
1890
2545
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -1908,7 +2563,7 @@ function checkSteeringFrontMatter(workspaceRoot) {
|
|
|
1908
2563
|
}
|
|
1909
2564
|
function checkSingleSteering(filePath, issues) {
|
|
1910
2565
|
try {
|
|
1911
|
-
const content =
|
|
2566
|
+
const content = fs17.readFileSync(filePath, "utf-8");
|
|
1912
2567
|
const lines = content.split("\n");
|
|
1913
2568
|
if (lines[0]?.trim() !== "---") return;
|
|
1914
2569
|
let endIdx = -1;
|
|
@@ -1932,16 +2587,16 @@ function fixSteeringWhitespace(steeringDir) {
|
|
|
1932
2587
|
const walkAndFix = (dir) => {
|
|
1933
2588
|
let entries;
|
|
1934
2589
|
try {
|
|
1935
|
-
entries =
|
|
2590
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
1936
2591
|
} catch {
|
|
1937
2592
|
return;
|
|
1938
2593
|
}
|
|
1939
2594
|
for (const entry of entries) {
|
|
1940
|
-
const fullPath =
|
|
2595
|
+
const fullPath = path16.join(dir, entry.name);
|
|
1941
2596
|
if (entry.isDirectory()) {
|
|
1942
2597
|
walkAndFix(fullPath);
|
|
1943
2598
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1944
|
-
const content =
|
|
2599
|
+
const content = fs17.readFileSync(fullPath, "utf-8");
|
|
1945
2600
|
const lines = content.split("\n");
|
|
1946
2601
|
if (lines[0]?.trim() !== "---") continue;
|
|
1947
2602
|
let endIdx = -1;
|
|
@@ -1961,7 +2616,7 @@ function fixSteeringWhitespace(steeringDir) {
|
|
|
1961
2616
|
}
|
|
1962
2617
|
}
|
|
1963
2618
|
if (changed) {
|
|
1964
|
-
|
|
2619
|
+
fs17.writeFileSync(fullPath, lines.join("\n"), "utf-8");
|
|
1965
2620
|
}
|
|
1966
2621
|
}
|
|
1967
2622
|
}
|
|
@@ -1969,12 +2624,12 @@ function fixSteeringWhitespace(steeringDir) {
|
|
|
1969
2624
|
walkAndFix(steeringDir);
|
|
1970
2625
|
}
|
|
1971
2626
|
function checkMetadataJson(workspaceRoot) {
|
|
1972
|
-
const metaPath =
|
|
1973
|
-
if (!
|
|
2627
|
+
const metaPath = path16.join(workspaceRoot, ".kiro/metadata.json");
|
|
2628
|
+
if (!fs17.existsSync(metaPath)) {
|
|
1974
2629
|
return { name: "metadata-json", result: "WARN", message: ".kiro/metadata.json not found (optional)" };
|
|
1975
2630
|
}
|
|
1976
2631
|
try {
|
|
1977
|
-
const content =
|
|
2632
|
+
const content = fs17.readFileSync(metaPath, "utf-8");
|
|
1978
2633
|
const parsed = JSON.parse(content);
|
|
1979
2634
|
if (!parsed.version || !parsed.name) {
|
|
1980
2635
|
return { name: "metadata-json", result: "FAIL", message: ".kiro/metadata.json missing required fields (version, name)" };
|
|
@@ -1988,12 +2643,12 @@ function checkStatuslineExecBit(workspaceRoot) {
|
|
|
1988
2643
|
if (process11.platform === "win32") {
|
|
1989
2644
|
return { name: "statusline-exec", result: "PASS", message: "Statusline exec bit check (skipped on Windows)" };
|
|
1990
2645
|
}
|
|
1991
|
-
const shPath =
|
|
1992
|
-
if (!
|
|
2646
|
+
const shPath = path16.join(workspaceRoot, ".kiro/statusline.sh");
|
|
2647
|
+
if (!fs17.existsSync(shPath)) {
|
|
1993
2648
|
return { name: "statusline-exec", result: "PASS", message: "No statusline.sh to check" };
|
|
1994
2649
|
}
|
|
1995
2650
|
try {
|
|
1996
|
-
const stat =
|
|
2651
|
+
const stat = fs17.statSync(shPath);
|
|
1997
2652
|
const isExecutable = (stat.mode & 73) !== 0;
|
|
1998
2653
|
if (isExecutable) {
|
|
1999
2654
|
return { name: "statusline-exec", result: "PASS", message: "statusline.sh is executable" };
|
|
@@ -2004,7 +2659,7 @@ function checkStatuslineExecBit(workspaceRoot) {
|
|
|
2004
2659
|
message: "statusline.sh is not executable",
|
|
2005
2660
|
fixable: true,
|
|
2006
2661
|
fixAction: () => {
|
|
2007
|
-
|
|
2662
|
+
fs17.chmodSync(shPath, 493);
|
|
2008
2663
|
}
|
|
2009
2664
|
};
|
|
2010
2665
|
} catch {
|
|
@@ -2013,24 +2668,24 @@ function checkStatuslineExecBit(workspaceRoot) {
|
|
|
2013
2668
|
}
|
|
2014
2669
|
|
|
2015
2670
|
// src/commands/telemetry.ts
|
|
2016
|
-
import
|
|
2017
|
-
import
|
|
2671
|
+
import fs18 from "fs";
|
|
2672
|
+
import path17 from "path";
|
|
2018
2673
|
import os2 from "os";
|
|
2019
2674
|
import process12 from "process";
|
|
2020
|
-
var CONFIG_DIR =
|
|
2021
|
-
var CONFIG_FILE =
|
|
2675
|
+
var CONFIG_DIR = path17.join(os2.homedir(), ".kiro-kit");
|
|
2676
|
+
var CONFIG_FILE = path17.join(CONFIG_DIR, "config.json");
|
|
2022
2677
|
function readConfig() {
|
|
2023
2678
|
try {
|
|
2024
|
-
if (
|
|
2025
|
-
return JSON.parse(
|
|
2679
|
+
if (fs18.existsSync(CONFIG_FILE)) {
|
|
2680
|
+
return JSON.parse(fs18.readFileSync(CONFIG_FILE, "utf-8"));
|
|
2026
2681
|
}
|
|
2027
2682
|
} catch {
|
|
2028
2683
|
}
|
|
2029
2684
|
return { telemetry: false };
|
|
2030
2685
|
}
|
|
2031
2686
|
function writeConfig(config) {
|
|
2032
|
-
|
|
2033
|
-
|
|
2687
|
+
fs18.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
2688
|
+
fs18.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2034
2689
|
}
|
|
2035
2690
|
function registerTelemetryCommand(program2) {
|
|
2036
2691
|
const telemetry = program2.command("telemetry").description("Manage anonymous usage telemetry");
|