@sayknow-cli/coding-agent 0.5.11 → 0.5.14

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/types/cli/setup-cli.d.ts +1 -1
  3. package/dist/types/config/file-lock.d.ts +8 -0
  4. package/dist/types/config/model-registry.d.ts +1 -0
  5. package/dist/types/config/models-config-schema.d.ts +2 -0
  6. package/dist/types/defaults/skc-ui-skills.d.ts +12 -0
  7. package/dist/types/hooks/ui-skill-keywords.d.ts +84 -0
  8. package/dist/types/session-import/redact.d.ts +1 -1
  9. package/dist/types/setup/external-ui-skills.d.ts +37 -0
  10. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +13 -1
  11. package/dist/types/skc-runtime/ultragoal-succession.d.ts +251 -0
  12. package/dist/types/skc-runtime/workflow-placeholder.d.ts +6 -0
  13. package/package.json +7 -7
  14. package/src/cli/setup-cli.ts +22 -1
  15. package/src/cli/skills-cli.ts +29 -10
  16. package/src/commands/setup.ts +1 -0
  17. package/src/config/file-lock-gc.ts +39 -2
  18. package/src/config/file-lock.ts +22 -1
  19. package/src/config/model-registry.ts +116 -40
  20. package/src/config/models-config-schema.ts +1 -0
  21. package/src/defaults/skc/skills/ultragoal/SKILL.md +3 -0
  22. package/src/defaults/skc/ui-skills/LICENSE.appllama +21 -0
  23. package/src/defaults/skc/ui-skills/LICENSE.emilkowalski +21 -0
  24. package/src/defaults/skc/ui-skills/NOTICE.md +43 -0
  25. package/src/defaults/skc/ui-skills/animate/SKILL.md +536 -0
  26. package/src/defaults/skc/ui-skills/animation-vocabulary/SKILL.md +178 -0
  27. package/src/defaults/skc/ui-skills/apple-design/SKILL.md +285 -0
  28. package/src/defaults/skc/ui-skills/appllama-app-design-skill/SKILL.md +821 -0
  29. package/src/defaults/skc/ui-skills/ask-sonner/SKILL.md +157 -0
  30. package/src/defaults/skc/ui-skills/emil-design-eng/SKILL.md +671 -0
  31. package/src/defaults/skc/ui-skills/find-animation-opportunities/SKILL.md +137 -0
  32. package/src/defaults/skc/ui-skills/improve-animations/SKILL.md +305 -0
  33. package/src/defaults/skc/ui-skills/mobile-native/SKILL.md +308 -0
  34. package/src/defaults/skc/ui-skills/pick-ui-library/SKILL.md +82 -0
  35. package/src/defaults/skc/ui-skills/prototype/SKILL.md +300 -0
  36. package/src/defaults/skc/ui-skills/react-bits/SKILL.md +113 -0
  37. package/src/defaults/skc/ui-skills/review-animations/SKILL.md +312 -0
  38. package/src/defaults/skc-ui-skills.ts +108 -0
  39. package/src/extensibility/runtime-skill-discovery.ts +8 -2
  40. package/src/hooks/native-skill-hook.ts +14 -0
  41. package/src/hooks/ui-skill-keywords.ts +312 -0
  42. package/src/internal-urls/docs-index.generated.ts +1 -1
  43. package/src/prompts/agents/architect.md +1 -0
  44. package/src/prompts/agents/critic.md +1 -0
  45. package/src/prompts/agents/executor.md +1 -0
  46. package/src/prompts/agents/planner.md +1 -0
  47. package/src/prompts/system/system-prompt.md +1 -0
  48. package/src/prompts/tools/skill.md +2 -2
  49. package/src/sdk/session.ts +7 -5
  50. package/src/session-import/redact.ts +11 -3
  51. package/src/setup/external-ui-skills.ts +132 -0
  52. package/src/skc-runtime/state-writer.ts +24 -27
  53. package/src/skc-runtime/ultragoal-runtime.ts +125 -45
  54. package/src/skc-runtime/ultragoal-succession.ts +1667 -0
  55. package/src/skc-runtime/workflow-placeholder.ts +33 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.5.14] - 2026-09-16
6
+
7
+ ### Added
8
+
9
+ - Bundled frontend UI/UX craft skills so SKC sessions can invoke them without a separate user install: Emil Kowalski's web craft set (`emil-design-eng`, `animate`, `review-animations`, `improve-animations`, `find-animation-opportunities`, `pick-ui-library`, `prototype`, `mobile-native`, `animation-vocabulary`, `apple-design`, `ask-sonner`) and Appllama's native mobile build bar (`appllama-app-design-skill`). These are not public workflow skills; the four workflows remain `deep-interview`, `ralplan`, `ultragoal`, and `team`. `appllama-usage` is deliberately excluded because it only drives the paid Appllama MCP, which SKC does not ship. Each skill's companion reference files are inlined as appendices by `scripts/build-ui-skill-bundle.ts`, since only `SKILL.md` is compiled into the binary and a bundled skill's `baseDir` is a virtual path.
10
+ - The native `UserPromptSubmit` hook routes frontend UI/UX prompts to the matching bundled UI skill automatically, in English and Korean, so the agent loads `animate`, `review-animations`, `pick-ui-library`, `mobile-native`, `appllama-app-design-skill`, `ask-sonner`, or the `emil-design-eng` baseline without the user naming a skill. Detection is additive to workflow-keyword activation, seeds no workflow mode state, and stays silent for non-frontend prompts and for SKC's own terminal-only UI work.
11
+ - Transition-shaped prompts route to the `transitions-dev` skill ([transitions.dev](https://github.com/Jakubantalik/transitions.dev)). When it is absent the agent installs it itself with upstream's own non-interactive command (`-a universal`, landing in `<project>/.agents/skills/`, a root SKC loads) through the `bash` tool, so the user never types an install command; when present it is loaded directly. The collection is deliberately **not** vendored: upstream licenses only its CLI and Refine tooling under MIT, while the transitions carry a clause forbidding repackaging or publishing the collection — which is what bundling into the SKC binary would do. Installing into the user's own project is explicitly permitted.
12
+ - `skc setup ui-skills` installs the routed-but-unbundled frontend skills into the current project (`<project>/.agents/skills/`, a root SKC's agents provider loads), with `--check` to audit and `--force` to reinstall. It runs upstream's own publisher CLI and verifies the payload landed, so a silent installer failure is reported instead of being treated as success.
13
+ - Animated-React-component prompts route to a new bundled `react-bits` skill covering the [React Bits](https://reactbits.dev) registry: when to reach for it, the per-component `npx shadcn@latest add @react-bits/<Component>-<Variant>` install, the `components.json` precondition that otherwise hangs a non-interactive run, post-install duties (dependency weight, reduced-motion guards), and a catalog of all 169 component names. The skill is SKC-authored guidance plus factual metadata — React Bits ships no agent skill of its own, and its MIT + Commons Clause license forbids redistributing the components, so no component source is bundled.
14
+
15
+ ### Fixed
16
+
17
+ - Runtime skill discovery ranks project `.skc/skills` ahead of the user-home catalog before applying the result limit, so a populated `~/.skc/skills` cannot hide a project skill (ported from upstream v0.16.5 skill-convention budget ranking).
18
+ - Session-import URL redaction no longer caps the scheme at 16 characters. The scheme-character run is boundary-anchored so long and digit-prefixed credential URLs still redact without quadratic scanning (ported from upstream #5402).
19
+
20
+ ## [0.5.13] - 2026-09-15
21
+ ### Fixed
22
+
23
+ - Session-import's cross-process idempotency test no longer depends on Bun child stdout surviving a contended file lock. The child writes its result to a file as well as stdout, so a silent empty pipe on Linux CI cannot fail a successful import.
24
+
25
+
26
+ ## [0.5.12] - 2026-09-15
27
+ ### Added
28
+
29
+ - `skc ultragoal succession offer|adopt|status` gives an approved Ultragoal an explicit, audited path from the repository it was planned in to the repository its implementation belongs to (ported from upstream #5353). The source run fences the selected unfinished goals immediately; the target adopts a fresh pending plan with verbatim brief/objectives and unresolved obligations as provenance, not inherited completion authority.
30
+ - Custom OpenAI-compatible providers in `models.yml` auto-populate `/model` from their live `/v1/models` catalog without a manual `discovery:` block, and auto-classify each discovered model's wire API family (ported from upstream #5187). Explicit `discovery` config and the local `openaiCompat` proxy lane are unchanged.
31
+
32
+ ### Fixed
33
+
34
+ - Session import redaction now bounds the URL-credential scheme to 16 characters so large credential-free transcripts scan in linear time instead of quadratic (ported from upstream #5346). Markdown-wrapped `https://user:pass@host` credentials remain redacted.
35
+ - File-lock acquisition timeouts are a typed `FileLockAcquireError` with the lock path, retry count, and holder, instead of an untyped string. Empty `.lock` directories left by a crash between `mkdir` and writing `info` are reclaimable by GC (the SKC-shaped subset of upstream #5378/#5381; this tree has no `.lock.pending.<pid>.<uuid>` staging).
36
+ - `writeGuardedJsonAtomic` honors `lockHeld`, so callers already inside `withWorkflowStateLock` (succession offer/adopt, start/checkpoint) no longer self-deadlock.
37
+ - Under tmux, a successful sixel DA1 no longer turns INLINE transcript images back on. Ghostty answers that query with ";4" even though it never paints sixel, so every screenshot was smuggled through DCS passthrough onto the outer image plane and stacked over the chat. The probe now enables overlay sixel (the pet) only; inline sixel stays off unless tmux itself owns `terminal-features=sixel`.
38
+
39
+
5
40
  ## [0.5.11] - 2026-09-10
6
41
 
7
42
  ### Fixed
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { AuthStorage, SqliteAuthCredentialStore } from "@sayknow-cli/ai";
7
7
  import { runExternalCredentialAutoImport } from "../setup/credential-auto-import";
8
- export type SetupComponent = "claude" | "codex" | "credentials" | "defaults" | "hermes" | "hooks" | "provider" | "python" | "stt";
8
+ export type SetupComponent = "claude" | "codex" | "credentials" | "defaults" | "hermes" | "hooks" | "provider" | "python" | "stt" | "ui-skills";
9
9
  export interface SetupCommandArgs {
10
10
  component: SetupComponent;
11
11
  flags: {
@@ -3,6 +3,14 @@ export interface FileLockOptions {
3
3
  retries?: number;
4
4
  retryDelayMs?: number;
5
5
  }
6
+ export declare class FileLockAcquireError extends Error {
7
+ readonly filePath: string;
8
+ readonly lockPath: string;
9
+ readonly attempts: number;
10
+ readonly holder: string;
11
+ readonly code = "acquire_timeout";
12
+ constructor(filePath: string, lockPath: string, attempts: number, holder: string);
13
+ }
6
14
  /**
7
15
  * Returns the OS-provided process start timestamp for PID-reuse detection.
8
16
  * `ps` is available on the supported Unix hosts (macOS and Linux), unlike
@@ -97,6 +97,7 @@ export declare const ModelsConfigFile: ConfigFile<{
97
97
  auth?: "none" | "apiKey" | "oauth" | undefined;
98
98
  discovery?: {
99
99
  type: "ollama" | "lm-studio" | "omlx" | "sglang" | "llama.cpp" | "openai-models-list";
100
+ apiByModelPrefix?: Record<string, string> | undefined;
100
101
  } | undefined;
101
102
  requestTransform?: {
102
103
  profile?: "openai-proxy" | undefined;
@@ -265,6 +265,7 @@ export declare const ProviderDiscoverySchema: z.ZodObject<{
265
265
  "llama.cpp": "llama.cpp";
266
266
  "openai-models-list": "openai-models-list";
267
267
  }>;
268
+ apiByModelPrefix: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
268
269
  }, z.core.$strip>;
269
270
  export declare const ProviderAuthSchema: z.ZodEnum<{
270
271
  none: "none";
@@ -375,6 +376,7 @@ export declare const ModelsConfigSchema: z.ZodObject<{
375
376
  "llama.cpp": "llama.cpp";
376
377
  "openai-models-list": "openai-models-list";
377
378
  }>;
379
+ apiByModelPrefix: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
378
380
  }, z.core.$strip>>;
379
381
  requestTransform: z.ZodOptional<z.ZodObject<{
380
382
  profile: z.ZodOptional<z.ZodEnum<{
@@ -0,0 +1,12 @@
1
+ import type { Skill } from "../extensibility/skills";
2
+ /**
3
+ * Bundled frontend UI/UX craft skills. These are NOT public SKC workflow skills.
4
+ * The four public workflows remain deep-interview, ralplan, ultragoal, and team.
5
+ *
6
+ * Sources and deliberate exclusions are documented in
7
+ * `skc/ui-skills/NOTICE.md`; regenerate with `scripts/build-ui-skill-bundle.ts`.
8
+ */
9
+ export declare const BUNDLED_SKC_UI_SKILL_NAMES: readonly ["emil-design-eng", "animate", "review-animations", "improve-animations", "find-animation-opportunities", "pick-ui-library", "prototype", "mobile-native", "animation-vocabulary", "apple-design", "ask-sonner", "appllama-app-design-skill", "react-bits"];
10
+ export type BundledSkcUiSkillName = (typeof BUNDLED_SKC_UI_SKILL_NAMES)[number];
11
+ export declare function getEmbeddedSkcUiSkills(): Skill[];
12
+ export declare function withEmbeddedSkcUiSkills(skills: Skill[]): Skill[];
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Automatic frontend UI/UX skill routing.
3
+ *
4
+ * The bundled UI craft skills (see `defaults/skc-ui-skills.ts`) are compiled
5
+ * into the binary, so the agent can invoke them without any user install. This
6
+ * module decides WHEN: the native `UserPromptSubmit` hook matches the prompt
7
+ * against the patterns below and injects a directive naming the skill to load.
8
+ *
9
+ * Unlike the canonical workflow skills, UI skills have no `skc state` mode
10
+ * state. Detection here never seeds workflow state; it only advertises the
11
+ * skill for the current turn.
12
+ *
13
+ * Patterns cover English and Korean because prompts arrive in both.
14
+ */
15
+ import type { BundledSkcUiSkillName } from "../defaults/skc-ui-skills";
16
+ export interface UiSkillKeywordDefinition {
17
+ skill: BundledSkcUiSkillName;
18
+ /** Higher wins when several patterns match the same prompt. */
19
+ priority: number;
20
+ pattern: RegExp;
21
+ }
22
+ /** Base craft skill paired with any specialized web frontend match. */
23
+ export declare const UI_SKILL_BASE: BundledSkcUiSkillName;
24
+ /**
25
+ * Ordered specialized detectors. The first (highest priority) match selects the
26
+ * lead skill; `emil-design-eng` is added as the craft baseline for build and
27
+ * review work.
28
+ */
29
+ export declare const UI_SKILL_KEYWORD_DEFINITIONS: readonly UiSkillKeywordDefinition[];
30
+ export interface UiSkillKeywordMatch {
31
+ skill: BundledSkcUiSkillName;
32
+ keyword: string;
33
+ priority: number;
34
+ }
35
+ /**
36
+ * Detect frontend UI/UX intent. Returns matches ordered by priority, most
37
+ * specific first, with `emil-design-eng` appended as the craft baseline.
38
+ */
39
+ export declare function detectUiSkillKeywords(text: string): UiSkillKeywordMatch[];
40
+ /**
41
+ * Build the `UserPromptSubmit` directive that makes the agent load the matching
42
+ * bundled UI skill for this turn. Returns null when the prompt is not frontend
43
+ * UI/UX work.
44
+ */
45
+ export declare function buildUiSkillActivationContext(text: string): string | null;
46
+ /**
47
+ * Frontend skills SKC routes to but deliberately does NOT vendor.
48
+ *
49
+ * `transitions.dev` publishes an agent skill, but its terms license only the
50
+ * CLI and Refine tooling under MIT; the transition collection itself carries a
51
+ * separate clause that forbids repackaging or publishing the collection (or a
52
+ * substantial part of it). Compiling it into the SKC binary and shipping that
53
+ * to every user is exactly the prohibited redistribution, while installing it
54
+ * into your own project is explicitly allowed. So SKC routes to a user-owned
55
+ * install and never carries the content.
56
+ */
57
+ export interface ExternalUiSkillDefinition {
58
+ skill: string;
59
+ priority: number;
60
+ pattern: RegExp;
61
+ /**
62
+ * Upstream's own non-interactive install. `-a universal` lands the skill in
63
+ * `<project>/.agents/skills/`, which SKC's agents provider loads. It is the
64
+ * agent that runs this through its normal bash tool, so the install stays a
65
+ * visible, permissioned tool call — never a silent fetch inside the hook.
66
+ */
67
+ installCommand: string;
68
+ source: string;
69
+ }
70
+ export declare const EXTERNAL_UI_SKILL_DEFINITIONS: readonly ExternalUiSkillDefinition[];
71
+ export declare function detectExternalUiSkills(text: string): ExternalUiSkillDefinition[];
72
+ /**
73
+ * Directive for frontend skills SKC routes to but does not vendor.
74
+ *
75
+ * Installed: load it. Missing: the agent runs upstream's own non-interactive
76
+ * install itself, so the user never types the command. The install runs through
77
+ * the agent's bash tool — visible and permissioned — rather than as a silent
78
+ * network fetch inside this hook.
79
+ */
80
+ export declare function buildExternalUiSkillContext(input: {
81
+ cwd: string;
82
+ home: string;
83
+ text: string;
84
+ }): Promise<string | null>;
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export declare const IMPORT_REDACTED_PLACEHOLDER = "[REDACTED]";
10
10
  /** Bumped when patterns change; persisted in import provenance. */
11
- export declare const IMPORT_SANITIZER_VERSION = 3;
11
+ export declare const IMPORT_SANITIZER_VERSION = 5;
12
12
  export interface ImportRedactionResult {
13
13
  value: string;
14
14
  redacted: number;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Install the frontend UI skills SKC routes to but cannot bundle.
3
+ *
4
+ * Most UI craft skills are compiled into the binary (see
5
+ * `defaults/skc-ui-skills.ts`). A few upstreams license their content for use
6
+ * but forbid redistributing the collection, so SKC must not carry them. For
7
+ * those, this installs upstream's own published payload into the project, where
8
+ * SKC's skill discovery picks it up like any other project skill.
9
+ *
10
+ * Target is `<project>/.agents/skills/<name>/`, which SKC's agents provider
11
+ * loads on the project walk-up.
12
+ */
13
+ export type ExternalUiSkillStatus = "installed" | "already-present" | "failed" | "missing";
14
+ export interface ExternalUiSkillResult {
15
+ skill: string;
16
+ source: string;
17
+ status: ExternalUiSkillStatus;
18
+ path: string;
19
+ detail?: string;
20
+ }
21
+ export interface ExternalUiSkillsReport {
22
+ projectDir: string;
23
+ installed: number;
24
+ alreadyPresent: number;
25
+ failed: number;
26
+ missing: number;
27
+ skills: ExternalUiSkillResult[];
28
+ }
29
+ export interface InstallExternalUiSkillsOptions {
30
+ cwd: string;
31
+ /** Report what would happen without running any installer. */
32
+ check?: boolean;
33
+ /** Reinstall even when the skill is already present. */
34
+ force?: boolean;
35
+ }
36
+ export declare function installExternalUiSkills(options: InstallExternalUiSkillsOptions): Promise<ExternalUiSkillsReport>;
37
+ export declare function formatExternalUiSkillsReport(report: ExternalUiSkillsReport): string;
@@ -264,7 +264,19 @@ export declare function recordUltragoalNudgeIfBudgetRemaining(input: {
264
264
  reason: string;
265
265
  currentGoalObjective?: string;
266
266
  }): Promise<UltragoalNudgeOutcome>;
267
- export declare function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string | null): Promise<void>;
267
+ export declare function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string | null, options?: {
268
+ lockHeld?: boolean;
269
+ }): Promise<void>;
270
+ /**
271
+ * The single exclusion that decides who owns an Ultragoal run.
272
+ *
273
+ * Establishing ownership is a read-decide-write sequence: a start reads the plan,
274
+ * checks the outgoing succession fence, then commits `active`. Every caller that
275
+ * decides ownership — source admission, succession offer, and successor
276
+ * publication — runs that decision inside this lock, keyed on the plan.
277
+ */
278
+ export declare function withUltragoalPlanOwnership<T>(cwd: string, sessionId: string, fn: () => Promise<T>): Promise<T>;
279
+ export declare function resolveUltragoalOwnershipSession(cwd: string, sessionId?: string | null): Promise<string>;
268
280
  export declare function nonEmptyString(value: unknown): string | null;
269
281
  export declare function stringArray(value: unknown): string[] | null;
270
282
  export declare function hashPipelineMetadata(metadata: Omit<UltragoalPipelineMetadata, "metadataHash">): string;
@@ -0,0 +1,251 @@
1
+ import { type RepositoryBinding } from "./repository-binding";
2
+ import { type UltragoalCommandResult, type UltragoalGoalStatus, type UltragoalPlan, type UltragoalSkcGoalMode } from "./ultragoal-runtime";
3
+ export declare const ULTRAGOAL_SUCCESSION_OPERATION_SCHEMA: "skc.ultragoal_succession_operation.v1";
4
+ export declare const ULTRAGOAL_SUCCESSION_OFFER_SCHEMA: "skc.ultragoal_succession_offer.v1";
5
+ export declare const ULTRAGOAL_SUCCESSION_FENCE_SCHEMA: "skc.ultragoal_succession_fence.v1";
6
+ export declare const ULTRAGOAL_SUCCESSION_ADOPTION_SCHEMA: "skc.ultragoal_succession_adoption.v1";
7
+ export declare const ULTRAGOAL_SUCCESSION_CLAIM_SCHEMA: "skc.ultragoal_succession_claim.v1";
8
+ export declare const SUCCESSION_ADOPTED_EVENT: "succession_adopted";
9
+ export type UltragoalSuccessionErrorCode = "source_plan_missing" | "source_not_quiescent" | "invalid_selection" | "unsafe_target" | "authorization_required" | "divergent_operation" | "goal_handed_off" | "adoption_unpublished" | "publication_conflict" | "published_plan_missing" | "fence_missing" | "fence_mismatch" | "source_changed" | "offer_untrusted" | "offer_path_escape" | "target_mismatch" | "target_occupied" | "duplicate_adoption";
10
+ export declare class UltragoalSuccessionError extends Error {
11
+ readonly code: UltragoalSuccessionErrorCode;
12
+ constructor(code: UltragoalSuccessionErrorCode, message: string);
13
+ }
14
+ export interface UltragoalSuccessionArtifactDigests {
15
+ briefSha256: string;
16
+ goalsSha256: string;
17
+ ledgerSha256: string;
18
+ }
19
+ export interface UltragoalSuccessionCarriedGoal {
20
+ sourceGoalId: string;
21
+ title: string;
22
+ /** The source objective, verbatim. Never a summary. */
23
+ objective: string;
24
+ /** Historical provenance only; never a receipt. */
25
+ sourceStatusAtOffer: UltragoalGoalStatus;
26
+ unresolvedObligations: string[];
27
+ /**
28
+ * Source dependency groups this goal belonged to, carried as an obligation
29
+ * with explicit id mapping. The group's *metadata* (batch hashes, receipts)
30
+ * is deliberately not carried — that would be inherited validation authority.
31
+ * What is carried is the requirement that these goals be validated together.
32
+ */
33
+ dependencyGroups: UltragoalSuccessionDependencyGroup[];
34
+ }
35
+ export type UltragoalSuccessionDependencyGroupKind = "validation-batch" | "review-blocker";
36
+ export interface UltragoalSuccessionDependencyGroup {
37
+ kind: UltragoalSuccessionDependencyGroupKind;
38
+ groupId: string;
39
+ memberSourceGoalIds: string[];
40
+ /** For validation batches: the source goal that closed the batch. */
41
+ finalSourceGoalId?: string;
42
+ }
43
+ /**
44
+ * Per-selected-goal snapshot of exactly what was offered.
45
+ *
46
+ * Admission compares *this*, not whole-file equality. The fence deliberately
47
+ * leaves unselected goals schedulable, so ordinary source progress moves
48
+ * `goals.json`/`ledger.jsonl` bytes; requiring whole-file equality would
49
+ * permanently strand the selected work through drift that never touched it
50
+ * (issue #5353 review item 3). Whole-file digests remain recorded as provenance
51
+ * and any observed drift is written into the adoption record.
52
+ */
53
+ export interface UltragoalSuccessionSelectionSnapshot {
54
+ /** The brief carries global constraints, so it must not change. */
55
+ briefSha256: string;
56
+ goals: Array<{
57
+ sourceGoalId: string;
58
+ recordSha256: string;
59
+ obligationsSha256: string;
60
+ }>;
61
+ snapshotSha256: string;
62
+ }
63
+ export interface UltragoalSuccessionOffer {
64
+ schema: typeof ULTRAGOAL_SUCCESSION_OFFER_SCHEMA;
65
+ operationId: string;
66
+ createdAt: string;
67
+ source: {
68
+ sessionId: string;
69
+ repository: RepositoryBinding;
70
+ /** Whole-file digests at offer time. Provenance; not the admission test. */
71
+ artifacts: UltragoalSuccessionArtifactDigests;
72
+ /** The authoritative admission test for "did the offered work change". */
73
+ selectionSnapshot: UltragoalSuccessionSelectionSnapshot;
74
+ skcGoalMode: UltragoalSkcGoalMode;
75
+ };
76
+ target: {
77
+ repository: RepositoryBinding;
78
+ };
79
+ selection: {
80
+ goalIds: string[];
81
+ };
82
+ carryover: {
83
+ /** The source brief, verbatim. */
84
+ brief: string;
85
+ goals: UltragoalSuccessionCarriedGoal[];
86
+ };
87
+ authorization: {
88
+ statement: string;
89
+ authorizedBy: string;
90
+ };
91
+ provenanceNotice: string;
92
+ }
93
+ export interface UltragoalSuccessionFence {
94
+ schema: typeof ULTRAGOAL_SUCCESSION_FENCE_SCHEMA;
95
+ operationId: string;
96
+ createdAt: string;
97
+ sourceSessionId: string;
98
+ sourceRepository: RepositoryBinding;
99
+ targetRepository: RepositoryBinding;
100
+ selectedGoalIds: string[];
101
+ sourceArtifacts: UltragoalSuccessionArtifactDigests;
102
+ offerPath: string;
103
+ offerSha256: string;
104
+ }
105
+ export interface UltragoalSuccessionGoalMapping {
106
+ sourceGoalId: string;
107
+ targetGoalId: string;
108
+ }
109
+ export interface UltragoalSuccessionAdoption {
110
+ schema: typeof ULTRAGOAL_SUCCESSION_ADOPTION_SCHEMA;
111
+ operationId: string;
112
+ status: "pending" | "published";
113
+ claimedAt: string;
114
+ publishedAt?: string;
115
+ targetSessionId: string;
116
+ targetRepository: RepositoryBinding;
117
+ source: {
118
+ sessionId: string;
119
+ repository: RepositoryBinding;
120
+ artifacts: UltragoalSuccessionArtifactDigests;
121
+ /** Whole-file digests observed at adoption; may differ from the offer. */
122
+ artifactsAtAdoption: UltragoalSuccessionArtifactDigests;
123
+ /** Whole-file drift on goals the offer did not select, recorded honestly. */
124
+ unselectedDrift: string[];
125
+ verifiedAt: string;
126
+ };
127
+ goalMap: UltragoalSuccessionGoalMapping[];
128
+ /**
129
+ * Content digest of the plan this operation is entitled to publish, recorded
130
+ * before publication. A pending replay may only proceed when the target plan
131
+ * is absent or matches this exactly; anything else is someone else's work.
132
+ */
133
+ expectedPlanDigest: string;
134
+ /** Pinned so the published plan is a deterministic function of the claim. */
135
+ plannedAt: string;
136
+ offerPath: string;
137
+ offerSha256: string;
138
+ fencePath: string;
139
+ claimPath: string;
140
+ authorization: {
141
+ statement: string;
142
+ authorizedBy: string;
143
+ };
144
+ provenanceNotice: string;
145
+ }
146
+ /**
147
+ * Repository-wide adoption claim.
148
+ *
149
+ * The session-scoped adoption record cannot be the exclusion primitive: two
150
+ * simultaneous sessions in one repository each create their own file and both
151
+ * win. This claim is a single O_EXCL file per operation for the whole
152
+ * repository, so exclusion is atomic rather than scan-then-write.
153
+ */
154
+ export interface UltragoalSuccessionRepositoryClaim {
155
+ schema: typeof ULTRAGOAL_SUCCESSION_CLAIM_SCHEMA;
156
+ operationId: string;
157
+ targetSessionId: string;
158
+ targetWorktreeRoot: string;
159
+ targetCommonDir: string | null;
160
+ claimedAt: string;
161
+ }
162
+ export declare function ultragoalSuccessionDir(cwd: string, sessionId: string): string;
163
+ export declare function ultragoalSuccessionFencePath(cwd: string, sessionId: string): string;
164
+ export declare function ultragoalSuccessionAdoptionPath(cwd: string, sessionId: string): string;
165
+ export declare function ultragoalSuccessionOfferPath(cwd: string, sessionId: string, operationId: string): string;
166
+ /**
167
+ * Repository-wide (deliberately NOT session-scoped) adoption claim.
168
+ *
169
+ * Session directories cannot express "this repository has already adopted this
170
+ * operation", so the exclusion primitive lives beside them under the shared
171
+ * ultragoal root that `getUltragoalPaths` already uses for session-less state.
172
+ */
173
+ export declare function ultragoalSuccessionClaimPath(cwd: string, operationId: string): string;
174
+ /** Read the durable outgoing ownership fence for a source session, if any. */
175
+ export declare function readUltragoalSuccessionFence(cwd: string, sessionId: string): Promise<UltragoalSuccessionFence | null>;
176
+ /** Read the durable adoption claim for a target session, if any. */
177
+ export declare function readUltragoalSuccessionAdoption(cwd: string, sessionId: string): Promise<UltragoalSuccessionAdoption | null>;
178
+ /**
179
+ * Source admission guard: a goal that has been handed off to a successor run is
180
+ * no longer this run's to schedule or checkpoint.
181
+ *
182
+ * Goals outside the fenced selection stay fully schedulable — the fence defines
183
+ * one owner per goal, not a freeze of the whole source run.
184
+ */
185
+ export declare function assertUltragoalGoalNotFenced(cwd: string, sessionId: string | null | undefined, goalId: string): Promise<void>;
186
+ /**
187
+ * Target admission guard: an adoption that has not finished publishing does not
188
+ * yet own anything.
189
+ *
190
+ * Publication writes `goals.json` before it marks the claim published, so a
191
+ * crash leaves a *visible plan with an unpublished claim*. A visible plan is not
192
+ * admission evidence — the claim is. Without this, the target executes goals
193
+ * whose adoption was never completed, and the run's own completion receipts
194
+ * would rest on a transaction that never committed.
195
+ *
196
+ * Callers must invoke this before any mutation so a refusal leaves `goals.json`
197
+ * and `ledger.jsonl` untouched.
198
+ */
199
+ export declare function assertUltragoalAdoptionPublished(cwd: string, sessionId: string | null | undefined): Promise<void>;
200
+ export interface UltragoalSuccessionOfferInput {
201
+ cwd: string;
202
+ sessionId?: string | null;
203
+ targetRepositoryPath: string;
204
+ goalIds: readonly string[];
205
+ authorization: string;
206
+ authorizedBy: string;
207
+ }
208
+ export interface UltragoalSuccessionOfferResult {
209
+ operationId: string;
210
+ offerPath: string;
211
+ fencePath: string;
212
+ offer: UltragoalSuccessionOffer;
213
+ /** True when an identical prior operation was reconciled instead of re-recorded. */
214
+ reconciled: boolean;
215
+ }
216
+ /**
217
+ * Record an explicit successor offer and fence the selected goals off from the
218
+ * source run. Writes nothing to the source brief, goals or ledger.
219
+ */
220
+ export declare function offerUltragoalSuccession(input: UltragoalSuccessionOfferInput): Promise<UltragoalSuccessionOfferResult>;
221
+ export interface UltragoalSuccessionAdoptInput {
222
+ cwd: string;
223
+ sessionId?: string | null;
224
+ offerPath: string;
225
+ skcGoalMode?: UltragoalSkcGoalMode;
226
+ }
227
+ export interface UltragoalSuccessionAdoptResult {
228
+ operationId: string;
229
+ adoptionPath: string;
230
+ plan: UltragoalPlan;
231
+ goalMap: UltragoalSuccessionGoalMapping[];
232
+ reconciled: boolean;
233
+ }
234
+ /**
235
+ * Adopt an explicit successor offer into this repository/session.
236
+ *
237
+ * Fails closed on a forged or stale offer, a changed source, a missing source
238
+ * fence, an unnamed target, an occupied target, and any duplicate or divergent
239
+ * adoption anywhere in this repository. A retry reconciles the exact recorded
240
+ * operation or fails closed; it never resumes a different one.
241
+ */
242
+ export declare function adoptUltragoalSuccession(input: UltragoalSuccessionAdoptInput): Promise<UltragoalSuccessionAdoptResult>;
243
+ /**
244
+ * `skc ultragoal succession <offer|adopt|status>`.
245
+ *
246
+ * Kept out of `RECONCILE_COMMANDS` on purpose: the reconcile pass appends a
247
+ * `reconcile_failed` ledger row on failure, which would move the very source
248
+ * digests this feature records. The target's workflow state and HUD reconcile on
249
+ * the next ordinary `skc ultragoal status`.
250
+ */
251
+ export declare function runUltragoalSuccessionCommand(args: readonly string[], cwd: string): Promise<UltragoalCommandResult>;
@@ -0,0 +1,6 @@
1
+ /** Shared placeholder semantics for model-authored workflow content. */
2
+ export declare const WORKFLOW_PLACEHOLDER_CORRECTION = "provide a specific, non-placeholder question or objective instead of empty, whitespace, unused, TODO, TBD, N/A, N-A, NA, none, placeholder, or stub";
3
+ /** Exact, case-insensitive placeholder detection; meaningful sentences remain valid. */
4
+ export declare function isWorkflowPlaceholderText(value: unknown): boolean;
5
+ /** Detect a legacy formatted deep-interview ask whose visible body is a placeholder. */
6
+ export declare function isLegacyDeepInterviewPlaceholder(value: unknown): boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/coding-agent",
4
- "version": "0.5.11",
4
+ "version": "0.5.14",
5
5
  "description": "Sayknow-CLI CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://sayknow-cli.com",
7
7
  "author": "jaybeyond",
@@ -54,12 +54,12 @@
54
54
  "@agentclientprotocol/sdk": "1.3.0",
55
55
  "@babel/parser": "^7.29.3",
56
56
  "@mozilla/readability": "^0.6.0",
57
- "@sayknow-cli/stats": "0.5.11",
58
- "@sayknow-cli/agent-core": "0.5.11",
59
- "@sayknow-cli/ai": "0.5.11",
60
- "@sayknow-cli/natives": "0.5.11",
61
- "@sayknow-cli/tui": "0.5.11",
62
- "@sayknow-cli/utils": "0.5.11",
57
+ "@sayknow-cli/stats": "0.5.14",
58
+ "@sayknow-cli/agent-core": "0.5.14",
59
+ "@sayknow-cli/ai": "0.5.14",
60
+ "@sayknow-cli/natives": "0.5.14",
61
+ "@sayknow-cli/tui": "0.5.14",
62
+ "@sayknow-cli/utils": "0.5.14",
63
63
  "@puppeteer/browsers": "^2.13.0",
64
64
  "@types/turndown": "5.0.6",
65
65
  "@xterm/headless": "^6.0.0",
@@ -23,6 +23,7 @@ import {
23
23
  getAutoImportOAuthCredentialSkips,
24
24
  isAutoImportOAuthCredential,
25
25
  } from "../setup/credential-import";
26
+ import { formatExternalUiSkillsReport, installExternalUiSkills } from "../setup/external-ui-skills";
26
27
  import {
27
28
  formatHermesSetupResult,
28
29
  type HermesSetupFlags,
@@ -46,7 +47,8 @@ export type SetupComponent =
46
47
  | "hooks"
47
48
  | "provider"
48
49
  | "python"
49
- | "stt";
50
+ | "stt"
51
+ | "ui-skills";
50
52
 
51
53
  export interface SetupCommandArgs {
52
54
  component: SetupComponent;
@@ -290,6 +292,25 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
290
292
  case "credentials":
291
293
  await handleCredentialsSetup(cmd.flags);
292
294
  break;
295
+ case "ui-skills":
296
+ await handleUiSkillsSetup(cmd.flags);
297
+ break;
298
+ }
299
+ }
300
+
301
+ async function handleUiSkillsSetup(flags: SetupCommandArgs["flags"]): Promise<void> {
302
+ const report = await installExternalUiSkills({
303
+ cwd: process.cwd(),
304
+ check: flags.check,
305
+ force: flags.force,
306
+ });
307
+ if (flags.json) {
308
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
309
+ } else {
310
+ process.stdout.write(`${formatExternalUiSkillsReport(report)}\n`);
311
+ }
312
+ if (report.failed > 0 || (flags.check && report.missing > 0)) {
313
+ process.exitCode = 1;
293
314
  }
294
315
  }
295
316