@sayknow-cli/coding-agent 0.2.4 → 0.2.6
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 +24 -0
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/cli/update-cli.d.ts +3 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +27 -0
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/lsp/startup-events.d.ts +1 -0
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/skc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/skc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/skc-runtime/session-layout.d.ts +59 -0
- package/dist/types/skc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/skc-runtime/state-graph.d.ts +1 -1
- package/dist/types/skc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/skc-runtime/state-schema.d.ts +2 -0
- package/dist/types/skc-runtime/state-writer.d.ts +36 -7
- package/dist/types/skc-runtime/tmux-sessions.d.ts +2 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +7 -4
- package/dist/types/skc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/skc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +5 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/package.json +7 -7
- package/scripts/build-binary.ts +0 -7
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli/setup-cli.ts +14 -1
- package/src/cli/update-cli.ts +53 -3
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/launch.ts +1 -1
- package/src/commands/migrate.ts +46 -0
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/config/model-registry.ts +9 -2
- package/src/config/model-resolver.ts +13 -2
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +17 -0
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/defaults/skc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/skc/skills/deep-interview/SKILL.md +30 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +10 -4
- package/src/defaults/skc/skills/team/SKILL.md +51 -47
- package/src/defaults/skc/skills/ultragoal/SKILL.md +17 -13
- package/src/exec/bash-executor.ts +3 -1
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/skc-plugins/injection.ts +23 -4
- package/src/extensibility/skc-plugins/state.ts +16 -1
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +11 -9
- package/src/lsp/startup-events.ts +24 -0
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/components/welcome.ts +20 -9
- package/src/modes/controllers/input-controller.ts +21 -3
- package/src/modes/interactive-mode.ts +27 -19
- package/src/modes/prompt-action-autocomplete.ts +11 -1
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +7 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/session/agent-session.ts +43 -41
- package/src/session/session-manager.ts +19 -2
- package/src/setup/hermes/templates/operator-instructions.v1.md +8 -0
- package/src/setup/hermes-setup.ts +1 -1
- package/src/skc-runtime/deep-interview-recorder.ts +51 -18
- package/src/skc-runtime/deep-interview-runtime.ts +49 -23
- package/src/skc-runtime/goal-mode-request.ts +26 -11
- package/src/skc-runtime/launch-tmux.ts +68 -15
- package/src/skc-runtime/ralplan-runtime.ts +79 -50
- package/src/skc-runtime/session-layout.ts +180 -0
- package/src/skc-runtime/session-resolution.ts +217 -0
- package/src/skc-runtime/state-graph.ts +1 -2
- package/src/skc-runtime/state-migrations.ts +1 -0
- package/src/skc-runtime/state-runtime.ts +237 -114
- package/src/skc-runtime/state-schema.ts +2 -0
- package/src/skc-runtime/state-writer.ts +310 -42
- package/src/skc-runtime/team-runtime.ts +43 -19
- package/src/skc-runtime/tmux-sessions.ts +43 -2
- package/src/skc-runtime/ultragoal-guard.ts +45 -2
- package/src/skc-runtime/ultragoal-runtime.ts +121 -41
- package/src/skc-runtime/workflow-command-ref.ts +1 -2
- package/src/skc-runtime/workflow-manifest.ts +1 -2
- package/src/skill-state/active-state.ts +116 -129
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +238 -111
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/slash-commands/builtin-registry.ts +8 -4
- package/src/system-prompt.ts +11 -9
- package/src/task/agents.ts +1 -22
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask.ts +34 -12
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/computer.ts +58 -4
- package/src/utils/edit-mode.ts +1 -1
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `skc migrate` — import MCP servers and skills from other coding agents.
|
|
3
|
+
*/
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { getAgentDir, getMCPConfigPath, getProjectAgentDir, getProjectDir } from "@sayknow-cli/utils";
|
|
7
|
+
import { planMigration } from "../migrate/action-planner";
|
|
8
|
+
import { getAdapter } from "../migrate/adapters/index";
|
|
9
|
+
import { executeActions } from "../migrate/executor";
|
|
10
|
+
import { buildReport, renderHuman } from "../migrate/report";
|
|
11
|
+
import {
|
|
12
|
+
type AdapterResult,
|
|
13
|
+
CANONICAL_SOURCE_ORDER,
|
|
14
|
+
MIGRATE_SOURCES,
|
|
15
|
+
type MigrateDestinations,
|
|
16
|
+
type MigrateReport,
|
|
17
|
+
type MigrateSource,
|
|
18
|
+
} from "../migrate/types";
|
|
19
|
+
|
|
20
|
+
export interface MigrateCommandArgs {
|
|
21
|
+
from: string[];
|
|
22
|
+
project: boolean;
|
|
23
|
+
force: boolean;
|
|
24
|
+
dryRun: boolean;
|
|
25
|
+
json: boolean;
|
|
26
|
+
/** Test seam: override home dir for source discovery. */
|
|
27
|
+
homeDir?: string;
|
|
28
|
+
/** Test seam: override cwd for project-scope destinations. */
|
|
29
|
+
cwd?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class MigrateArgsError extends Error {}
|
|
33
|
+
|
|
34
|
+
/** Expand `all`/repeated `--from`, validate, and return sources in canonical order. */
|
|
35
|
+
export function resolveSources(from: string[]): MigrateSource[] {
|
|
36
|
+
if (from.length === 0) {
|
|
37
|
+
throw new MigrateArgsError("No source selected. Use --from <claude-code|codex|opencode|all> (repeatable).");
|
|
38
|
+
}
|
|
39
|
+
const selected = new Set<MigrateSource>();
|
|
40
|
+
for (const raw of from) {
|
|
41
|
+
const value = raw.trim().toLowerCase();
|
|
42
|
+
if (value === "all") {
|
|
43
|
+
for (const s of MIGRATE_SOURCES) selected.add(s);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!(MIGRATE_SOURCES as readonly string[]).includes(value)) {
|
|
47
|
+
throw new MigrateArgsError(`Unknown source "${raw}". Valid: ${MIGRATE_SOURCES.join(", ")}, all.`);
|
|
48
|
+
}
|
|
49
|
+
selected.add(value as MigrateSource);
|
|
50
|
+
}
|
|
51
|
+
return CANONICAL_SOURCE_ORDER.filter(s => selected.has(s));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveDestinations(project: boolean, cwd: string): MigrateDestinations {
|
|
55
|
+
const scope = project ? "project" : "user";
|
|
56
|
+
const skillsDir = project ? path.join(getProjectAgentDir(cwd), "skills") : path.join(getAgentDir(), "skills");
|
|
57
|
+
return { mcpConfigPath: getMCPConfigPath(scope, cwd), skillsDir };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Run the migration and return the report (does not set process.exitCode). */
|
|
61
|
+
export async function runMigrate(args: MigrateCommandArgs): Promise<MigrateReport> {
|
|
62
|
+
const sources = resolveSources(args.from);
|
|
63
|
+
const cwd = args.cwd ?? getProjectDir();
|
|
64
|
+
const homeDir = args.homeDir ?? os.homedir();
|
|
65
|
+
const destinations = resolveDestinations(args.project, cwd);
|
|
66
|
+
|
|
67
|
+
const results: AdapterResult[] = [];
|
|
68
|
+
for (const source of sources) {
|
|
69
|
+
results.push(await getAdapter(source).collect({ homeDir }));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { actions, warnings } = await planMigration({ results, destinations, force: args.force });
|
|
73
|
+
const finalActions = args.dryRun ? actions : await executeActions(actions);
|
|
74
|
+
|
|
75
|
+
return buildReport({
|
|
76
|
+
actions: finalActions,
|
|
77
|
+
warnings,
|
|
78
|
+
sources,
|
|
79
|
+
destinations,
|
|
80
|
+
dryRun: args.dryRun,
|
|
81
|
+
project: args.project,
|
|
82
|
+
force: args.force,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** CLI entry: run, render, and set the process exit code. */
|
|
87
|
+
export async function runMigrateCommand(args: MigrateCommandArgs): Promise<void> {
|
|
88
|
+
let report: MigrateReport;
|
|
89
|
+
try {
|
|
90
|
+
report = await runMigrate(args);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof MigrateArgsError) {
|
|
93
|
+
process.stderr.write(`${error.message}\n`);
|
|
94
|
+
process.exitCode = 2;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (args.json) {
|
|
101
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
102
|
+
} else {
|
|
103
|
+
process.stdout.write(`${renderHuman(report)}\n`);
|
|
104
|
+
}
|
|
105
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
106
|
+
}
|
package/src/cli/setup-cli.ts
CHANGED
|
@@ -374,6 +374,7 @@ async function handleHooksSetup(flags: { json?: boolean; check?: boolean }): Pro
|
|
|
374
374
|
async function handleDefaultsSetup(flags: { json?: boolean; check?: boolean; force?: boolean }): Promise<void> {
|
|
375
375
|
const result = await installDefaultSkcDefinitions({ check: flags.check, force: flags.force });
|
|
376
376
|
const hasCheckFailure = result.missing > 0 || result.different > 0;
|
|
377
|
+
const inspectGuidance = `Inspect bundled skills with: ${APP_NAME} skills list; read one with: ${APP_NAME} skills read ralplan`;
|
|
377
378
|
|
|
378
379
|
if (flags.json) {
|
|
379
380
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -388,18 +389,30 @@ async function handleDefaultsSetup(flags: { json?: boolean; check?: boolean; for
|
|
|
388
389
|
console.error(
|
|
389
390
|
chalk.dim(`Missing: ${result.missing}; different: ${result.different}; matching: ${result.matching}`),
|
|
390
391
|
);
|
|
392
|
+
console.error(chalk.dim(inspectGuidance));
|
|
393
|
+
console.error(
|
|
394
|
+
chalk.dim(
|
|
395
|
+
`Compare embedded defaults before overwriting local files with: ${APP_NAME} setup defaults --force`,
|
|
396
|
+
),
|
|
397
|
+
);
|
|
391
398
|
process.exit(1);
|
|
392
399
|
}
|
|
393
400
|
console.log(chalk.green(`${theme.status.success} Default SKC workflow skills are installed`));
|
|
394
401
|
console.log(chalk.dim(`Target: ${result.targetRoot}`));
|
|
402
|
+
console.log(chalk.dim(inspectGuidance));
|
|
395
403
|
return;
|
|
396
404
|
}
|
|
397
405
|
|
|
398
406
|
console.log(chalk.green(`${theme.status.success} Default SKC workflow skills installed`));
|
|
399
407
|
console.log(chalk.dim(`Target: ${result.targetRoot}`));
|
|
400
408
|
console.log(chalk.dim(`Written: ${result.written}; skipped: ${result.skipped}`));
|
|
409
|
+
console.log(chalk.dim(inspectGuidance));
|
|
401
410
|
if (result.skipped > 0 && !flags.force) {
|
|
402
|
-
console.log(
|
|
411
|
+
console.log(
|
|
412
|
+
chalk.dim(
|
|
413
|
+
`Existing local default workflow skill files were preserved. Use ${APP_NAME} setup defaults --force to overwrite them intentionally.`,
|
|
414
|
+
),
|
|
415
|
+
);
|
|
403
416
|
}
|
|
404
417
|
}
|
|
405
418
|
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface InstalledVersionVerification {
|
|
|
25
25
|
ok: boolean;
|
|
26
26
|
actual?: string;
|
|
27
27
|
path?: string;
|
|
28
|
+
smokeTestFailed?: boolean;
|
|
29
|
+
smokeTestOutput?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/** Paths and verifier used while replacing a downloaded binary update. */
|
|
@@ -226,6 +228,36 @@ async function verifyInstalledVersion(expectedVersion: string): Promise<Installe
|
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
|
|
231
|
+
async function verifyInstalledRuntime(expectedVersion: string): Promise<InstalledVersionVerification> {
|
|
232
|
+
const versionResult = await verifyInstalledVersion(expectedVersion);
|
|
233
|
+
if (!versionResult.ok || !versionResult.path) {
|
|
234
|
+
return versionResult;
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const smokeResult = await $`${versionResult.path} --smoke-test`.quiet().nothrow();
|
|
238
|
+
if (smokeResult.exitCode === 0) {
|
|
239
|
+
return versionResult;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
...versionResult,
|
|
243
|
+
ok: false,
|
|
244
|
+
smokeTestFailed: true,
|
|
245
|
+
smokeTestOutput: smokeResult.text().trim(),
|
|
246
|
+
};
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return {
|
|
249
|
+
...versionResult,
|
|
250
|
+
ok: false,
|
|
251
|
+
smokeTestFailed: true,
|
|
252
|
+
smokeTestOutput: error instanceof Error ? error.message : String(error),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function printRestartGuidance(): void {
|
|
258
|
+
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
259
|
+
}
|
|
260
|
+
|
|
229
261
|
function printVerifiedVersion(expectedVersion: string): void {
|
|
230
262
|
console.log(chalk.green(`\n${theme.status.success} Updated to ${expectedVersion}`));
|
|
231
263
|
}
|
|
@@ -289,20 +321,38 @@ export function formatManualUpdateInstructionsForTest(platform: NodeJS.Platform
|
|
|
289
321
|
return formatManualUpdateInstructions(platform);
|
|
290
322
|
}
|
|
291
323
|
|
|
324
|
+
function normalizeVerificationOutput(output: string | undefined): string {
|
|
325
|
+
return output?.replace(/\s+/g, " ").trim() ?? "";
|
|
326
|
+
}
|
|
327
|
+
|
|
292
328
|
function formatVerificationFailure(result: InstalledVersionVerification, expectedVersion: string): string {
|
|
329
|
+
if (result.smokeTestFailed) {
|
|
330
|
+
const output = normalizeVerificationOutput(result.smokeTestOutput);
|
|
331
|
+
const outputSuffix = output ? `: ${output}` : "";
|
|
332
|
+
const pathSuffix = result.path ? ` at ${result.path}` : "";
|
|
333
|
+
return `${APP_NAME}${pathSuffix} reports ${result.actual ?? expectedVersion}, but --smoke-test failed${outputSuffix}. Close running ${APP_NAME} sessions and reinstall to repair a stale or partial update.`;
|
|
334
|
+
}
|
|
293
335
|
if (result.actual) {
|
|
294
336
|
return `${APP_NAME} at ${result.path} still reports ${result.actual} (expected ${expectedVersion})`;
|
|
295
337
|
}
|
|
296
338
|
return `could not verify updated version${result.path ? ` at ${result.path}` : ""}`;
|
|
297
339
|
}
|
|
298
340
|
|
|
341
|
+
export function formatVerificationFailureForTest(
|
|
342
|
+
result: InstalledVersionVerification,
|
|
343
|
+
expectedVersion: string,
|
|
344
|
+
): string {
|
|
345
|
+
return formatVerificationFailure(result, expectedVersion);
|
|
346
|
+
}
|
|
347
|
+
|
|
299
348
|
/**
|
|
300
349
|
* Print post-update verification result.
|
|
301
350
|
*/
|
|
302
351
|
async function printVerification(expectedVersion: string): Promise<void> {
|
|
303
|
-
const result = await
|
|
352
|
+
const result = await verifyInstalledRuntime(expectedVersion);
|
|
304
353
|
if (result.ok) {
|
|
305
354
|
printVerifiedVersion(expectedVersion);
|
|
355
|
+
printRestartGuidance();
|
|
306
356
|
return;
|
|
307
357
|
}
|
|
308
358
|
console.log(chalk.yellow(`\nWarning: ${formatVerificationFailure(result, expectedVersion)}`));
|
|
@@ -385,10 +435,10 @@ async function updateViaBinaryAt(targetPath: string, expectedVersion: string): P
|
|
|
385
435
|
tempPath,
|
|
386
436
|
backupPath,
|
|
387
437
|
expectedVersion,
|
|
388
|
-
verifyInstalledVersion,
|
|
438
|
+
verifyInstalledVersion: verifyInstalledRuntime,
|
|
389
439
|
});
|
|
390
440
|
printVerifiedVersion(expectedVersion);
|
|
391
|
-
|
|
441
|
+
printRestartGuidance();
|
|
392
442
|
}
|
|
393
443
|
|
|
394
444
|
/**
|
package/src/cli.ts
CHANGED
|
@@ -43,6 +43,7 @@ const commands: CommandEntry[] = [
|
|
|
43
43
|
load: () => import("./commands/contribution-prep").then(m => m.default),
|
|
44
44
|
},
|
|
45
45
|
{ name: "deep-interview", load: () => import("./commands/deep-interview").then(m => m.default) },
|
|
46
|
+
{ name: "migrate", load: () => import("./commands/migrate").then(m => m.default) },
|
|
46
47
|
{ name: "rlm", load: () => import("./commands/rlm").then(m => m.default) },
|
|
47
48
|
{ name: "update", load: () => import("./commands/update").then(m => m.default) },
|
|
48
49
|
{ name: "launch", load: () => import("./commands/launch").then(m => m.default) },
|
|
@@ -11,11 +11,11 @@ export default class DeepInterview extends Command {
|
|
|
11
11
|
threshold: Flags.string({ description: "Override ambiguity threshold for kickoff" }),
|
|
12
12
|
"threshold-source": Flags.string({ description: "Describe the threshold override source" }),
|
|
13
13
|
"session-id": Flags.string({
|
|
14
|
-
description: "Route state/spec handoff through a session-scoped .skc
|
|
14
|
+
description: "Route state/spec handoff through a session-scoped .skc/_session-{sessionid} directory",
|
|
15
15
|
}),
|
|
16
16
|
write: Flags.boolean({ description: "Persist a final deep-interview spec through the sanctioned SKC CLI/API" }),
|
|
17
17
|
stage: Flags.string({ description: 'Spec stage for --write (currently "final")' }),
|
|
18
|
-
slug: Flags.string({ description: "Safe slug for .skc/specs/deep-interview-<slug>.md" }),
|
|
18
|
+
slug: Flags.string({ description: "Safe slug for .skc/_session-{sessionid}/specs/deep-interview-<slug>.md" }),
|
|
19
19
|
spec: Flags.string({ description: "Final spec markdown or a path to the final spec markdown" }),
|
|
20
20
|
handoff: Flags.string({ description: 'After --write, hand off to a workflow target (currently "ralplan")' }),
|
|
21
21
|
deliberate: Flags.boolean({
|
package/src/commands/launch.ts
CHANGED
|
@@ -169,7 +169,7 @@ export default class Index extends Command {
|
|
|
169
169
|
rawArgs: launch.args,
|
|
170
170
|
cwd: launch.cwd,
|
|
171
171
|
worktreeBranch: launch.worktree.enabled && !launch.worktree.detached ? launch.worktree.branchName : null,
|
|
172
|
-
project: launch.
|
|
172
|
+
project: launch.cwd,
|
|
173
173
|
})
|
|
174
174
|
)
|
|
175
175
|
return;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import MCP servers and skills from other coding agents into SKC.
|
|
3
|
+
*/
|
|
4
|
+
import { Command, Flags } from "@sayknow-cli/utils/cli";
|
|
5
|
+
import { type MigrateCommandArgs, runMigrateCommand } from "../cli/migrate-cli";
|
|
6
|
+
|
|
7
|
+
export default class Migrate extends Command {
|
|
8
|
+
static description = "Import MCP servers and skills from Claude Code, Codex, or OpenCode";
|
|
9
|
+
|
|
10
|
+
static examples = [
|
|
11
|
+
"skc migrate --from claude-code",
|
|
12
|
+
"skc migrate --from codex --from opencode",
|
|
13
|
+
"skc migrate --from all --dry-run --json",
|
|
14
|
+
"skc migrate --from claude-code --project --force",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
static flags = {
|
|
18
|
+
from: Flags.string({
|
|
19
|
+
description: "Source agent to import from (repeatable): claude-code | codex | opencode | all",
|
|
20
|
+
multiple: true,
|
|
21
|
+
required: true,
|
|
22
|
+
}),
|
|
23
|
+
project: Flags.boolean({
|
|
24
|
+
description: "Write to the project scope (./.skc) instead of the user scope (~/.skc)",
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
27
|
+
force: Flags.boolean({
|
|
28
|
+
description: "Overwrite existing skills/MCP servers instead of skipping them",
|
|
29
|
+
default: false,
|
|
30
|
+
}),
|
|
31
|
+
"dry-run": Flags.boolean({ description: "Preview the migration without writing anything", default: false }),
|
|
32
|
+
json: Flags.boolean({ char: "j", description: "Emit a machine-readable JSON report", default: false }),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async run(): Promise<void> {
|
|
36
|
+
const { flags } = await this.parse(Migrate);
|
|
37
|
+
const cmd: MigrateCommandArgs = {
|
|
38
|
+
from: flags.from ?? [],
|
|
39
|
+
project: flags.project,
|
|
40
|
+
force: flags.force,
|
|
41
|
+
dryRun: flags["dry-run"],
|
|
42
|
+
json: flags.json,
|
|
43
|
+
};
|
|
44
|
+
await runMigrateCommand(cmd);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/commands/state.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Command } from "@sayknow-cli/utils/cli";
|
|
|
2
2
|
import { runNativeStateCommand } from "../skc-runtime/state-runtime";
|
|
3
3
|
|
|
4
4
|
export default class State extends Command {
|
|
5
|
-
static description =
|
|
5
|
+
static description =
|
|
6
|
+
"Read or update current-session SKC workflow state receipts under .skc/_session-{sessionid}/state";
|
|
6
7
|
static strict = false;
|
|
7
8
|
static examples = [
|
|
8
9
|
'$ skc state read --input \'{"mode":"deep-interview"}\' --json',
|
package/src/commands/team.ts
CHANGED
|
@@ -75,7 +75,7 @@ function parseInputFlag(argv: string[]): Record<string, unknown> {
|
|
|
75
75
|
|
|
76
76
|
export default class Team extends Command {
|
|
77
77
|
static description =
|
|
78
|
-
"Run native SKC tmux team orchestration from inside an existing tmux/SKC --tmux session; --dry-run writes ephemeral .skc/state/team state only";
|
|
78
|
+
"Run native SKC tmux team orchestration from inside an existing tmux/SKC --tmux session; --dry-run writes ephemeral .skc/_session-{sessionid}/state/team state only";
|
|
79
79
|
static strict = false;
|
|
80
80
|
|
|
81
81
|
static args = {
|
|
@@ -89,7 +89,7 @@ export default class Team extends Command {
|
|
|
89
89
|
json: Flags.boolean({ char: "j", description: "Emit machine-readable JSON", default: false }),
|
|
90
90
|
"dry-run": Flags.boolean({
|
|
91
91
|
description:
|
|
92
|
-
"Create ephemeral .skc/state/team state without starting tmux panes; do not commit generated state",
|
|
92
|
+
"Create ephemeral .skc/_session-{sessionid}/state/team state without starting tmux panes; do not commit generated state",
|
|
93
93
|
default: false,
|
|
94
94
|
}),
|
|
95
95
|
};
|
|
@@ -210,7 +210,11 @@ export default class Team extends Command {
|
|
|
210
210
|
`tmux: ${snapshot.tmux_session}`,
|
|
211
211
|
`state: ${snapshot.state_dir}`,
|
|
212
212
|
`workers: ${snapshot.workers.length}`,
|
|
213
|
-
...(dryRun
|
|
213
|
+
...(dryRun
|
|
214
|
+
? [
|
|
215
|
+
"dry-run: wrote ephemeral .skc/_session-{sessionid}/state/team state only; do not commit generated .skc state",
|
|
216
|
+
]
|
|
217
|
+
: []),
|
|
214
218
|
]);
|
|
215
219
|
}
|
|
216
220
|
}
|
|
@@ -872,7 +872,13 @@ const customReferenceMap = buildCustomReferenceMap();
|
|
|
872
872
|
|
|
873
873
|
function getCustomReferenceCandidateIds(modelId: string): string[] {
|
|
874
874
|
const candidates = new Set<string>();
|
|
875
|
-
const
|
|
875
|
+
const minimaxM = /^minimax-m(\d+(?:\.\d+)*)$/i.exec(modelId.trim());
|
|
876
|
+
const queue = minimaxM ? [`MiniMax-M${minimaxM[1]}`, modelId] : [modelId];
|
|
877
|
+
if (minimaxM) {
|
|
878
|
+
// MiniMax catalogs include lowercase wire ids plus display-cased aliases.
|
|
879
|
+
// Custom providers should keep the lowercase wire id while inheriting the
|
|
880
|
+
// canonical display casing when the alias exists.
|
|
881
|
+
}
|
|
876
882
|
for (let index = 0; index < queue.length; index += 1) {
|
|
877
883
|
const candidate = queue[index]?.trim();
|
|
878
884
|
if (!candidate || candidates.has(candidate)) continue;
|
|
@@ -1212,13 +1218,14 @@ export class ModelRegistry {
|
|
|
1212
1218
|
const existingIndex = indexByKey.get(key);
|
|
1213
1219
|
if (existingIndex !== undefined) {
|
|
1214
1220
|
const existingModel = merged[existingIndex];
|
|
1221
|
+
const referenceModel = resolveCustomModelReference(customModel.id);
|
|
1215
1222
|
merged[existingIndex] = enrichModelThinking({
|
|
1216
1223
|
...existingModel,
|
|
1217
1224
|
id: customModel.id,
|
|
1218
1225
|
provider: customModel.provider,
|
|
1219
1226
|
api: customModel.api,
|
|
1220
1227
|
baseUrl: customModel.baseUrl,
|
|
1221
|
-
name: customModel.name ?? existingModel.name,
|
|
1228
|
+
name: customModel.name ?? referenceModel?.name ?? existingModel.name,
|
|
1222
1229
|
reasoning: customModel.reasoning ?? existingModel.reasoning,
|
|
1223
1230
|
thinking: customModel.thinking ?? existingModel.thinking,
|
|
1224
1231
|
input: customModel.input ?? existingModel.input,
|
|
@@ -147,12 +147,23 @@ export function resolveProviderModelReference(
|
|
|
147
147
|
modelId: string,
|
|
148
148
|
availableModels: readonly Model<Api>[],
|
|
149
149
|
): Model<Api> | undefined {
|
|
150
|
-
const
|
|
151
|
-
const
|
|
150
|
+
const trimmedProvider = provider.trim();
|
|
151
|
+
const trimmedModelId = modelId.trim();
|
|
152
|
+
const normalizedProvider = trimmedProvider.toLowerCase();
|
|
153
|
+
const normalizedModelId = trimmedModelId.toLowerCase();
|
|
152
154
|
if (!normalizedProvider || !normalizedModelId) {
|
|
153
155
|
return undefined;
|
|
154
156
|
}
|
|
155
157
|
|
|
158
|
+
// Prefer an exact provider/model id match before falling back to the
|
|
159
|
+
// case-insensitive index. Some provider catalogs intentionally carry both a
|
|
160
|
+
// machine-id form (for example `minimax-m3`) and a display-cased alias
|
|
161
|
+
// (`MiniMax-M3`). The lowercase index correctly treats those aliases as
|
|
162
|
+
// ambiguous for fuzzy/case-insensitive lookup, but an exact selector should
|
|
163
|
+
// still resolve deterministically.
|
|
164
|
+
const caseExact = availableModels.find(m => m.provider === trimmedProvider && m.id === trimmedModelId);
|
|
165
|
+
if (caseExact) return caseExact;
|
|
166
|
+
|
|
156
167
|
const index = getProviderModelIndex(availableModels);
|
|
157
168
|
const exact = index.get(`${normalizedProvider}\u0000${normalizedModelId}`);
|
|
158
169
|
if (exact === null) {
|
|
@@ -22,6 +22,7 @@ const ReasoningEffortMapSchema = z.object({
|
|
|
22
22
|
export const OpenAICompatSchema = z.object({
|
|
23
23
|
supportsStore: z.boolean().optional(),
|
|
24
24
|
supportsDeveloperRole: z.boolean().optional(),
|
|
25
|
+
sendSessionHeaders: z.boolean().optional(),
|
|
25
26
|
supportsMultipleSystemMessages: z.boolean().optional(),
|
|
26
27
|
supportsReasoningEffort: z.boolean().optional(),
|
|
27
28
|
reasoningEffortMap: ReasoningEffortMapSchema.optional(),
|
|
@@ -1087,6 +1087,23 @@ export const SETTINGS_SCHEMA = {
|
|
|
1087
1087
|
},
|
|
1088
1088
|
},
|
|
1089
1089
|
|
|
1090
|
+
"startup.welcomeBannerMode": {
|
|
1091
|
+
type: "enum",
|
|
1092
|
+
values: ["auto", "unicode", "square", "ascii"] as const,
|
|
1093
|
+
default: "auto",
|
|
1094
|
+
ui: {
|
|
1095
|
+
tab: "interaction",
|
|
1096
|
+
label: "Welcome Banner Mode",
|
|
1097
|
+
description: "Logo style for the startup welcome screen",
|
|
1098
|
+
options: [
|
|
1099
|
+
{ value: "auto", label: "Auto", description: "Use the rounded Unicode logo" },
|
|
1100
|
+
{ value: "unicode", label: "Unicode", description: "Force the rounded Unicode logo" },
|
|
1101
|
+
{ value: "square", label: "Square Unicode", description: "Force the square-corner Unicode fallback" },
|
|
1102
|
+
{ value: "ascii", label: "ASCII", description: "Force the ASCII-safe logo" },
|
|
1103
|
+
],
|
|
1104
|
+
},
|
|
1105
|
+
},
|
|
1106
|
+
|
|
1090
1107
|
"startup.checkUpdate": {
|
|
1091
1108
|
type: "boolean",
|
|
1092
1109
|
default: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { coordinatorMcpStateRoot, skcRoot } from "../skc-runtime/session-layout";
|
|
3
4
|
|
|
4
5
|
export type CoordinatorMutationClass = "sessions" | "questions" | "reports";
|
|
5
6
|
|
|
@@ -73,9 +74,16 @@ function cleanScope(value: string | undefined): string | null {
|
|
|
73
74
|
return trimmed.replace(/[^a-zA-Z0-9_.-]+/g, "-").slice(0, 100) || null;
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function defaultCoordinatorMcpStateRoot(cwd: string, skcSessionId?: string): string {
|
|
78
|
+
return skcSessionId
|
|
79
|
+
? coordinatorMcpStateRoot(cwd, skcSessionId)
|
|
80
|
+
: path.join(skcRoot(cwd), "state", "coordinator-mcp");
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
export function buildCoordinatorMcpConfig(env: NodeJS.ProcessEnv = process.env): CoordinatorMcpConfig {
|
|
77
|
-
const
|
|
78
|
-
|
|
84
|
+
const stateRootOverride = env.SKC_COORDINATOR_MCP_STATE_ROOT?.trim();
|
|
85
|
+
const skcSessionId = env.SKC_SESSION_ID?.trim();
|
|
86
|
+
const stateRoot = stateRootOverride || defaultCoordinatorMcpStateRoot(process.cwd(), skcSessionId);
|
|
79
87
|
return {
|
|
80
88
|
allowedRoots: parseRootList(env.SKC_COORDINATOR_MCP_WORKDIR_ROOTS).map(root => path.resolve(root)),
|
|
81
89
|
mutationClasses: parseMutationClasses(
|