@dot-skill/workspace 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.js +446 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bharat Dudeja
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local skill workspace — git-like working tree for `.skill`.
|
|
3
|
+
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* .skill/
|
|
6
|
+
* config.json
|
|
7
|
+
* sections/<id>.json # proposed units (AI agent)
|
|
8
|
+
* index.json # staged ids
|
|
9
|
+
* HEAD.json # last compiled package
|
|
10
|
+
* objects/<id>.skill # continuity drafts / release packages
|
|
11
|
+
*/
|
|
12
|
+
import type { CompletenessReport, GenerationUsage, SectionType, SkillCompileProfile, SkillSource } from "@dot-skill/protocol";
|
|
13
|
+
import { type CompileResult } from "@dot-skill/core";
|
|
14
|
+
export declare const WORKSPACE_DIR = ".skill";
|
|
15
|
+
export interface WorkspaceSection {
|
|
16
|
+
kind: "section";
|
|
17
|
+
id: string;
|
|
18
|
+
type: SectionType;
|
|
19
|
+
title: string;
|
|
20
|
+
body: string;
|
|
21
|
+
fidelity: "exact" | "synthesize";
|
|
22
|
+
created_at: string;
|
|
23
|
+
updated_at: string;
|
|
24
|
+
/** Always agent-authored in valid workflows. */
|
|
25
|
+
source: "agent";
|
|
26
|
+
meta?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface WorkspaceIndex {
|
|
29
|
+
staged: string[];
|
|
30
|
+
updated_at: string;
|
|
31
|
+
}
|
|
32
|
+
export interface WorkspaceConfig {
|
|
33
|
+
version: 1;
|
|
34
|
+
title?: string;
|
|
35
|
+
default_stage_all: boolean;
|
|
36
|
+
created_at: string;
|
|
37
|
+
journey_summary?: string;
|
|
38
|
+
open_questions?: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface WorkspaceHead {
|
|
41
|
+
package_path?: string;
|
|
42
|
+
package_digest?: string;
|
|
43
|
+
skill_id?: string;
|
|
44
|
+
mint_status?: "draft" | "minted";
|
|
45
|
+
compile_profile?: SkillCompileProfile;
|
|
46
|
+
updated_at: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function requireAgentHost(host?: string): string;
|
|
49
|
+
export declare function findWorkspaceRoot(start?: string): string | undefined;
|
|
50
|
+
export declare function requireWorkspace(cwd?: string): string;
|
|
51
|
+
export declare function initWorkspace(cwd?: string, opts?: {
|
|
52
|
+
title?: string;
|
|
53
|
+
}): Promise<{
|
|
54
|
+
root: string;
|
|
55
|
+
created: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
export declare function loadConfig(root: string): Promise<WorkspaceConfig>;
|
|
58
|
+
export declare function saveConfig(root: string, config: WorkspaceConfig): Promise<void>;
|
|
59
|
+
export declare function loadIndex(root: string): Promise<WorkspaceIndex>;
|
|
60
|
+
export declare function loadHead(root: string): Promise<WorkspaceHead>;
|
|
61
|
+
export declare function listSections(root: string): Promise<WorkspaceSection[]>;
|
|
62
|
+
/** @deprecated use listSections */
|
|
63
|
+
export declare const listIngredients: typeof listSections;
|
|
64
|
+
export declare function proposeSection(root: string, input: {
|
|
65
|
+
title: string;
|
|
66
|
+
body: string;
|
|
67
|
+
type?: SectionType;
|
|
68
|
+
fidelity?: "exact" | "synthesize";
|
|
69
|
+
id?: string;
|
|
70
|
+
host?: string;
|
|
71
|
+
}): Promise<WorkspaceSection>;
|
|
72
|
+
/** @deprecated use proposeSection */
|
|
73
|
+
export declare const proposeIngredient: typeof proposeSection;
|
|
74
|
+
export declare function proposeMany(root: string, items: Array<{
|
|
75
|
+
title: string;
|
|
76
|
+
body: string;
|
|
77
|
+
type?: SectionType;
|
|
78
|
+
fidelity?: "exact" | "synthesize";
|
|
79
|
+
}>, host?: string): Promise<WorkspaceSection[]>;
|
|
80
|
+
export declare function setJourney(root: string, journey: {
|
|
81
|
+
summary: string;
|
|
82
|
+
open_questions?: string[];
|
|
83
|
+
}): Promise<WorkspaceConfig>;
|
|
84
|
+
export declare function stage(root: string, ids: string[] | "all"): Promise<WorkspaceIndex>;
|
|
85
|
+
export declare function unstage(root: string, ids: string[] | "all"): Promise<WorkspaceIndex>;
|
|
86
|
+
export interface StatusResult {
|
|
87
|
+
root: string;
|
|
88
|
+
title?: string;
|
|
89
|
+
unstaged: WorkspaceSection[];
|
|
90
|
+
staged: WorkspaceSection[];
|
|
91
|
+
head: WorkspaceHead;
|
|
92
|
+
completeness?: CompletenessReport;
|
|
93
|
+
journey_summary?: string;
|
|
94
|
+
agent_host_ok: boolean;
|
|
95
|
+
}
|
|
96
|
+
export declare function status(root: string): Promise<StatusResult>;
|
|
97
|
+
export interface CompileWorkspaceOptions {
|
|
98
|
+
title?: string;
|
|
99
|
+
summary?: string;
|
|
100
|
+
message?: string;
|
|
101
|
+
add_all?: boolean;
|
|
102
|
+
approve?: boolean;
|
|
103
|
+
mint?: boolean;
|
|
104
|
+
profile?: SkillCompileProfile;
|
|
105
|
+
host?: string;
|
|
106
|
+
generation_usage?: GenerationUsage;
|
|
107
|
+
input_tokens?: number;
|
|
108
|
+
output_tokens?: number;
|
|
109
|
+
}
|
|
110
|
+
export interface CompileWorkspaceResult {
|
|
111
|
+
source: SkillSource;
|
|
112
|
+
compile: CompileResult;
|
|
113
|
+
package_path: string;
|
|
114
|
+
minted?: boolean;
|
|
115
|
+
package_digest: string;
|
|
116
|
+
profile: SkillCompileProfile;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Continuity checkpoint — partial OK, privacy-scrubbed handoff draft.
|
|
120
|
+
*/
|
|
121
|
+
export declare function checkpoint(root: string, opts?: CompileWorkspaceOptions): Promise<CompileWorkspaceResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Compile staged sections → `.skill`.
|
|
124
|
+
* release profile refuses if incomplete; continuity allows partial.
|
|
125
|
+
*/
|
|
126
|
+
export declare function compileWorkspace(root: string, opts?: CompileWorkspaceOptions): Promise<CompileWorkspaceResult>;
|
|
127
|
+
/** @deprecated use compileWorkspace — Skillerr product term */
|
|
128
|
+
export declare function bake(root: string, opts?: CompileWorkspaceOptions & {
|
|
129
|
+
publish?: boolean;
|
|
130
|
+
}): Promise<CompileWorkspaceResult & {
|
|
131
|
+
published?: boolean;
|
|
132
|
+
}>;
|
|
133
|
+
export declare function discardSection(root: string, sectionId: string): Promise<void>;
|
|
134
|
+
/** @deprecated */
|
|
135
|
+
export declare const discardIngredient: typeof discardSection;
|
|
136
|
+
/** Load a continuity/release package for agent handoff resume. */
|
|
137
|
+
export declare function loadSkillHandoff(packagePath: string): Promise<{
|
|
138
|
+
skill_id: string;
|
|
139
|
+
title: string;
|
|
140
|
+
intent?: string;
|
|
141
|
+
journey?: unknown;
|
|
142
|
+
generation_usage?: unknown;
|
|
143
|
+
completeness?: CompletenessReport;
|
|
144
|
+
compile_profile?: SkillCompileProfile;
|
|
145
|
+
knowledge: Array<{
|
|
146
|
+
id: string;
|
|
147
|
+
title: string;
|
|
148
|
+
type: string;
|
|
149
|
+
}>;
|
|
150
|
+
open_questions?: string[];
|
|
151
|
+
mint_status?: string;
|
|
152
|
+
}>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local skill workspace — git-like working tree for `.skill`.
|
|
3
|
+
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* .skill/
|
|
6
|
+
* config.json
|
|
7
|
+
* sections/<id>.json # proposed units (AI agent)
|
|
8
|
+
* index.json # staged ids
|
|
9
|
+
* HEAD.json # last compiled package
|
|
10
|
+
* objects/<id>.skill # continuity drafts / release packages
|
|
11
|
+
*/
|
|
12
|
+
import { mkdir, readFile, writeFile, readdir, unlink } from "node:fs/promises";
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
16
|
+
import { isValidAgentHost, PROTOCOL_VERSION } from "@dot-skill/protocol";
|
|
17
|
+
import { compileSkillSource, approveCompilation, mintSkillPackage, redactSecrets, CompileRefusalError, } from "@dot-skill/core";
|
|
18
|
+
export const WORKSPACE_DIR = ".skill";
|
|
19
|
+
function id(prefix) {
|
|
20
|
+
return `${prefix}_${createHash("sha256").update(randomUUID()).digest("hex").slice(0, 12)}`;
|
|
21
|
+
}
|
|
22
|
+
export function requireAgentHost(host) {
|
|
23
|
+
const h = host ?? process.env.SKILL_HOST;
|
|
24
|
+
if (!isValidAgentHost(h)) {
|
|
25
|
+
throw new Error("AI agent provenance required. Set SKILL_HOST=cursor|ollama|lmstudio|llama-cpp|custom-agent|… (local models are supported).");
|
|
26
|
+
}
|
|
27
|
+
return h;
|
|
28
|
+
}
|
|
29
|
+
export function findWorkspaceRoot(start = process.cwd()) {
|
|
30
|
+
let dir = resolve(start);
|
|
31
|
+
for (;;) {
|
|
32
|
+
if (existsSync(join(dir, WORKSPACE_DIR, "config.json")))
|
|
33
|
+
return dir;
|
|
34
|
+
const parent = resolve(dir, "..");
|
|
35
|
+
if (parent === dir)
|
|
36
|
+
return undefined;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function requireWorkspace(cwd = process.cwd()) {
|
|
41
|
+
const root = findWorkspaceRoot(cwd);
|
|
42
|
+
if (!root) {
|
|
43
|
+
throw new Error(`Not a skill workspace (or any parent). Run: skill init`);
|
|
44
|
+
}
|
|
45
|
+
return root;
|
|
46
|
+
}
|
|
47
|
+
function paths(root) {
|
|
48
|
+
const base = join(root, WORKSPACE_DIR);
|
|
49
|
+
return {
|
|
50
|
+
base,
|
|
51
|
+
config: join(base, "config.json"),
|
|
52
|
+
index: join(base, "index.json"),
|
|
53
|
+
head: join(base, "HEAD.json"),
|
|
54
|
+
sections: join(base, "sections"),
|
|
55
|
+
/** @deprecated migrated from ingredients/ */
|
|
56
|
+
ingredientsLegacy: join(base, "ingredients"),
|
|
57
|
+
objects: join(base, "objects"),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export async function initWorkspace(cwd = process.cwd(), opts = {}) {
|
|
61
|
+
const root = resolve(cwd);
|
|
62
|
+
const p = paths(root);
|
|
63
|
+
if (existsSync(p.config)) {
|
|
64
|
+
return { root, created: false };
|
|
65
|
+
}
|
|
66
|
+
await mkdir(p.sections, { recursive: true });
|
|
67
|
+
await mkdir(p.objects, { recursive: true });
|
|
68
|
+
const config = {
|
|
69
|
+
version: 1,
|
|
70
|
+
title: opts.title,
|
|
71
|
+
default_stage_all: true,
|
|
72
|
+
created_at: new Date().toISOString(),
|
|
73
|
+
};
|
|
74
|
+
const index = { staged: [], updated_at: new Date().toISOString() };
|
|
75
|
+
const head = { updated_at: new Date().toISOString() };
|
|
76
|
+
await writeFile(p.config, JSON.stringify(config, null, 2) + "\n");
|
|
77
|
+
await writeFile(p.index, JSON.stringify(index, null, 2) + "\n");
|
|
78
|
+
await writeFile(p.head, JSON.stringify(head, null, 2) + "\n");
|
|
79
|
+
await writeFile(join(p.base, "README"), "Open .skill workspace. AI agent: skill propose → skill checkpoint|compile → skill mint.\nHuman reviews/stages; only agents create.\n");
|
|
80
|
+
return { root, created: true };
|
|
81
|
+
}
|
|
82
|
+
async function readJson(path) {
|
|
83
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
84
|
+
}
|
|
85
|
+
export async function loadConfig(root) {
|
|
86
|
+
return readJson(paths(root).config);
|
|
87
|
+
}
|
|
88
|
+
export async function saveConfig(root, config) {
|
|
89
|
+
await writeFile(paths(root).config, JSON.stringify(config, null, 2) + "\n");
|
|
90
|
+
}
|
|
91
|
+
export async function loadIndex(root) {
|
|
92
|
+
return readJson(paths(root).index);
|
|
93
|
+
}
|
|
94
|
+
export async function loadHead(root) {
|
|
95
|
+
const p = paths(root).head;
|
|
96
|
+
if (!existsSync(p))
|
|
97
|
+
return { updated_at: new Date().toISOString() };
|
|
98
|
+
return readJson(p);
|
|
99
|
+
}
|
|
100
|
+
async function listSectionFiles(root) {
|
|
101
|
+
const p = paths(root);
|
|
102
|
+
const dirs = [p.sections, p.ingredientsLegacy].filter((d) => existsSync(d));
|
|
103
|
+
const out = [];
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
for (const dir of dirs) {
|
|
106
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith(".json"));
|
|
107
|
+
for (const f of files) {
|
|
108
|
+
const raw = await readJson(join(dir, f));
|
|
109
|
+
const section = {
|
|
110
|
+
kind: "section",
|
|
111
|
+
id: raw.id,
|
|
112
|
+
type: raw.type,
|
|
113
|
+
title: raw.title,
|
|
114
|
+
body: raw.body,
|
|
115
|
+
fidelity: raw.fidelity ?? "exact",
|
|
116
|
+
created_at: raw.created_at,
|
|
117
|
+
updated_at: raw.updated_at,
|
|
118
|
+
source: "agent",
|
|
119
|
+
meta: raw.meta,
|
|
120
|
+
};
|
|
121
|
+
if (!seen.has(section.id)) {
|
|
122
|
+
seen.add(section.id);
|
|
123
|
+
out.push(section);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out.sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
128
|
+
}
|
|
129
|
+
export async function listSections(root) {
|
|
130
|
+
return listSectionFiles(root);
|
|
131
|
+
}
|
|
132
|
+
/** @deprecated use listSections */
|
|
133
|
+
export const listIngredients = listSections;
|
|
134
|
+
export async function proposeSection(root, input) {
|
|
135
|
+
requireAgentHost(input.host);
|
|
136
|
+
const section = {
|
|
137
|
+
kind: "section",
|
|
138
|
+
id: input.id ?? id("sec"),
|
|
139
|
+
type: input.type ?? "implementation_note",
|
|
140
|
+
title: input.title,
|
|
141
|
+
body: input.body,
|
|
142
|
+
fidelity: input.fidelity ?? "exact",
|
|
143
|
+
created_at: new Date().toISOString(),
|
|
144
|
+
updated_at: new Date().toISOString(),
|
|
145
|
+
source: "agent",
|
|
146
|
+
};
|
|
147
|
+
await mkdir(paths(root).sections, { recursive: true });
|
|
148
|
+
await writeFile(join(paths(root).sections, `${section.id}.json`), JSON.stringify(section, null, 2) + "\n");
|
|
149
|
+
const config = await loadConfig(root);
|
|
150
|
+
if (config.default_stage_all) {
|
|
151
|
+
await stage(root, [section.id]);
|
|
152
|
+
}
|
|
153
|
+
return section;
|
|
154
|
+
}
|
|
155
|
+
/** @deprecated use proposeSection */
|
|
156
|
+
export const proposeIngredient = proposeSection;
|
|
157
|
+
export async function proposeMany(root, items, host) {
|
|
158
|
+
requireAgentHost(host);
|
|
159
|
+
const out = [];
|
|
160
|
+
for (const item of items) {
|
|
161
|
+
out.push(await proposeSection(root, { ...item, host }));
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
export async function setJourney(root, journey) {
|
|
166
|
+
const config = await loadConfig(root);
|
|
167
|
+
config.journey_summary = redactSecrets(journey.summary);
|
|
168
|
+
config.open_questions = journey.open_questions?.map(redactSecrets);
|
|
169
|
+
await saveConfig(root, config);
|
|
170
|
+
return config;
|
|
171
|
+
}
|
|
172
|
+
export async function stage(root, ids) {
|
|
173
|
+
const all = await listSections(root);
|
|
174
|
+
const byId = new Map(all.map((i) => [i.id, i]));
|
|
175
|
+
const index = await loadIndex(root);
|
|
176
|
+
const add = ids === "all" ? all.map((i) => i.id) : ids;
|
|
177
|
+
for (const secId of add) {
|
|
178
|
+
if (!byId.has(secId))
|
|
179
|
+
throw new Error(`Unknown section: ${secId}`);
|
|
180
|
+
if (!index.staged.includes(secId))
|
|
181
|
+
index.staged.push(secId);
|
|
182
|
+
}
|
|
183
|
+
index.updated_at = new Date().toISOString();
|
|
184
|
+
await writeFile(paths(root).index, JSON.stringify(index, null, 2) + "\n");
|
|
185
|
+
return index;
|
|
186
|
+
}
|
|
187
|
+
export async function unstage(root, ids) {
|
|
188
|
+
const index = await loadIndex(root);
|
|
189
|
+
if (ids === "all")
|
|
190
|
+
index.staged = [];
|
|
191
|
+
else
|
|
192
|
+
index.staged = index.staged.filter((x) => !ids.includes(x));
|
|
193
|
+
index.updated_at = new Date().toISOString();
|
|
194
|
+
await writeFile(paths(root).index, JSON.stringify(index, null, 2) + "\n");
|
|
195
|
+
return index;
|
|
196
|
+
}
|
|
197
|
+
export async function status(root) {
|
|
198
|
+
const config = await loadConfig(root);
|
|
199
|
+
const index = await loadIndex(root);
|
|
200
|
+
const all = await listSections(root);
|
|
201
|
+
const stagedSet = new Set(index.staged);
|
|
202
|
+
const staged = all.filter((i) => stagedSet.has(i.id));
|
|
203
|
+
let completeness;
|
|
204
|
+
try {
|
|
205
|
+
if (staged.length) {
|
|
206
|
+
const source = await toSkillSource(root, staged, "status", "continuity");
|
|
207
|
+
const { assessCompleteness } = await import("@dot-skill/core");
|
|
208
|
+
completeness = assessCompleteness(source, {
|
|
209
|
+
profile: "release",
|
|
210
|
+
hasWorkflowAction: staged.some((s) => ["integration", "prompt", "implementation_note", "workflow_note", "code"].includes(s.type)),
|
|
211
|
+
hasKnowledge: staged.length > 0,
|
|
212
|
+
hasInputsDeclared: true,
|
|
213
|
+
pendingApprovals: [],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// status should not throw on incomplete
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
root,
|
|
222
|
+
title: config.title,
|
|
223
|
+
staged,
|
|
224
|
+
unstaged: all.filter((i) => !stagedSet.has(i.id)),
|
|
225
|
+
head: await loadHead(root),
|
|
226
|
+
completeness,
|
|
227
|
+
journey_summary: config.journey_summary,
|
|
228
|
+
agent_host_ok: isValidAgentHost(process.env.SKILL_HOST),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async function toSkillSource(root, staged, title, profile, usage, hostOverride) {
|
|
232
|
+
const host = requireAgentHost(hostOverride);
|
|
233
|
+
const config = await loadConfig(root);
|
|
234
|
+
const sections = staged.map((i) => ({
|
|
235
|
+
id: i.id,
|
|
236
|
+
revision: 1,
|
|
237
|
+
type: i.type,
|
|
238
|
+
title: i.title,
|
|
239
|
+
body: i.body,
|
|
240
|
+
attachments: [],
|
|
241
|
+
code_refs: [],
|
|
242
|
+
sensitivity: "shareable_redacted",
|
|
243
|
+
authored_by: "agent",
|
|
244
|
+
}));
|
|
245
|
+
const sid = id("src");
|
|
246
|
+
const hash = "sha256:" +
|
|
247
|
+
createHash("sha256")
|
|
248
|
+
.update(title + staged.map((s) => s.body).join(""))
|
|
249
|
+
.digest("hex");
|
|
250
|
+
return {
|
|
251
|
+
kind: "skill_source",
|
|
252
|
+
id: sid,
|
|
253
|
+
hash,
|
|
254
|
+
title,
|
|
255
|
+
summary: config.journey_summary ?? title,
|
|
256
|
+
intent: config.title ?? title,
|
|
257
|
+
sections,
|
|
258
|
+
steering: [],
|
|
259
|
+
prompts: [],
|
|
260
|
+
code_refs: [],
|
|
261
|
+
parents: [],
|
|
262
|
+
agent: {
|
|
263
|
+
host,
|
|
264
|
+
provider: process.env.SKILL_PROVIDER,
|
|
265
|
+
model: process.env.SKILL_MODEL,
|
|
266
|
+
runtime: process.env.SKILL_AGENT_RUNTIME ?? "@dot-skill/cli",
|
|
267
|
+
deployment: process.env.SKILL_DEPLOYMENT ?? "unknown",
|
|
268
|
+
endpoint: process.env.SKILL_ENDPOINT
|
|
269
|
+
? redactSecrets(process.env.SKILL_ENDPOINT)
|
|
270
|
+
: undefined,
|
|
271
|
+
session_ids: process.env.SKILL_SESSION_ID ? [process.env.SKILL_SESSION_ID] : [],
|
|
272
|
+
},
|
|
273
|
+
journey: {
|
|
274
|
+
summary: config.journey_summary ??
|
|
275
|
+
`Human+AI work on "${title}" (${staged.length} sections). Profile=${profile}.`,
|
|
276
|
+
open_questions: config.open_questions,
|
|
277
|
+
decisions: staged.filter((s) => s.type === "decision").map((s) => s.title),
|
|
278
|
+
redacted: true,
|
|
279
|
+
sensitivity: "shareable_redacted",
|
|
280
|
+
},
|
|
281
|
+
generation_usage: usage,
|
|
282
|
+
inputs_declared: "none",
|
|
283
|
+
sensitivity: "shareable_redacted",
|
|
284
|
+
created_at: new Date().toISOString(),
|
|
285
|
+
actor: { id: process.env.SKILL_ACTOR ?? "human" },
|
|
286
|
+
source_protocol_version: PROTOCOL_VERSION,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Continuity checkpoint — partial OK, privacy-scrubbed handoff draft.
|
|
291
|
+
*/
|
|
292
|
+
export async function checkpoint(root, opts = {}) {
|
|
293
|
+
return compileWorkspace(root, { ...opts, profile: "continuity", mint: false });
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Compile staged sections → `.skill`.
|
|
297
|
+
* release profile refuses if incomplete; continuity allows partial.
|
|
298
|
+
*/
|
|
299
|
+
export async function compileWorkspace(root, opts = {}) {
|
|
300
|
+
requireAgentHost(opts.host);
|
|
301
|
+
const profile = opts.profile ?? "release";
|
|
302
|
+
if (opts.add_all !== false) {
|
|
303
|
+
await stage(root, "all");
|
|
304
|
+
}
|
|
305
|
+
const st = await status(root);
|
|
306
|
+
if (st.staged.length === 0) {
|
|
307
|
+
throw new Error("Nothing staged. Agent must propose sections first (`skill propose`), then `skill add`.");
|
|
308
|
+
}
|
|
309
|
+
const title = opts.title ?? opts.message ?? (await loadConfig(root)).title ?? st.staged[0].title;
|
|
310
|
+
if (opts.summary) {
|
|
311
|
+
await setJourney(root, { summary: opts.summary, open_questions: undefined });
|
|
312
|
+
}
|
|
313
|
+
const usage = opts.generation_usage ??
|
|
314
|
+
(opts.input_tokens || opts.output_tokens
|
|
315
|
+
? {
|
|
316
|
+
input_tokens: opts.input_tokens,
|
|
317
|
+
output_tokens: opts.output_tokens,
|
|
318
|
+
total_tokens: (opts.input_tokens ?? 0) + (opts.output_tokens ?? 0),
|
|
319
|
+
reported_by: "agent",
|
|
320
|
+
captured_at: new Date().toISOString(),
|
|
321
|
+
host: process.env.SKILL_HOST,
|
|
322
|
+
model: process.env.SKILL_MODEL,
|
|
323
|
+
}
|
|
324
|
+
: parseUsageFromEnv());
|
|
325
|
+
const source = await toSkillSource(root, st.staged, title, profile, usage, opts.host);
|
|
326
|
+
let compiled;
|
|
327
|
+
try {
|
|
328
|
+
compiled = compileSkillSource(source, {
|
|
329
|
+
title,
|
|
330
|
+
description: opts.summary ?? opts.message,
|
|
331
|
+
profile,
|
|
332
|
+
approve_inferred_inputs: opts.approve === true,
|
|
333
|
+
approve_permissions: opts.approve === true,
|
|
334
|
+
generation_usage: usage,
|
|
335
|
+
provenance_mode: profile === "continuity" ? "redacted" : "full",
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
if (e instanceof CompileRefusalError) {
|
|
340
|
+
throw e;
|
|
341
|
+
}
|
|
342
|
+
throw e;
|
|
343
|
+
}
|
|
344
|
+
if (opts.approve === true) {
|
|
345
|
+
compiled = approveCompilation(compiled, { inputs: ["*"], permissions: true });
|
|
346
|
+
}
|
|
347
|
+
let bytes = compiled.packageBytes;
|
|
348
|
+
let minted = false;
|
|
349
|
+
if (opts.mint) {
|
|
350
|
+
if (profile !== "release") {
|
|
351
|
+
throw new Error("Mint only allowed with --profile release (not continuity drafts).");
|
|
352
|
+
}
|
|
353
|
+
const sealed = mintSkillPackage(compiled.files, {
|
|
354
|
+
host: requireAgentHost(opts.host),
|
|
355
|
+
provider: process.env.SKILL_PROVIDER,
|
|
356
|
+
model: process.env.SKILL_MODEL,
|
|
357
|
+
deployment: process.env.SKILL_DEPLOYMENT ?? "unknown",
|
|
358
|
+
endpoint: process.env.SKILL_ENDPOINT
|
|
359
|
+
? redactSecrets(process.env.SKILL_ENDPOINT)
|
|
360
|
+
: undefined,
|
|
361
|
+
actors: [process.env.SKILL_ACTOR ?? "human"],
|
|
362
|
+
agent_runtime: process.env.SKILL_AGENT_RUNTIME ?? "@dot-skill/cli",
|
|
363
|
+
});
|
|
364
|
+
bytes = sealed.packageBytes;
|
|
365
|
+
compiled = { ...compiled, files: sealed.files, packageBytes: sealed.packageBytes };
|
|
366
|
+
minted = true;
|
|
367
|
+
}
|
|
368
|
+
const digest = compiled.files.manifest.package_digest;
|
|
369
|
+
const outName = `${compiled.files.manifest.id}.skill`;
|
|
370
|
+
const package_path = join(paths(root).objects, outName);
|
|
371
|
+
await writeFile(package_path, bytes);
|
|
372
|
+
const head = {
|
|
373
|
+
package_path,
|
|
374
|
+
package_digest: digest,
|
|
375
|
+
skill_id: compiled.files.manifest.id,
|
|
376
|
+
mint_status: minted ? "minted" : "draft",
|
|
377
|
+
compile_profile: profile,
|
|
378
|
+
updated_at: new Date().toISOString(),
|
|
379
|
+
};
|
|
380
|
+
await writeFile(paths(root).head, JSON.stringify(head, null, 2) + "\n");
|
|
381
|
+
await writeFile(join(root, outName), bytes);
|
|
382
|
+
return {
|
|
383
|
+
source,
|
|
384
|
+
compile: compiled,
|
|
385
|
+
package_path,
|
|
386
|
+
minted,
|
|
387
|
+
package_digest: digest,
|
|
388
|
+
profile,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
/** @deprecated use compileWorkspace — Skillerr product term */
|
|
392
|
+
export async function bake(root, opts = {}) {
|
|
393
|
+
if (opts.publish) {
|
|
394
|
+
throw new Error("publish is not part of the open protocol happy path. Share the .skill file, or use a product registry later.");
|
|
395
|
+
}
|
|
396
|
+
return compileWorkspace(root, opts);
|
|
397
|
+
}
|
|
398
|
+
function parseUsageFromEnv() {
|
|
399
|
+
const input = process.env.SKILL_INPUT_TOKENS
|
|
400
|
+
? Number(process.env.SKILL_INPUT_TOKENS)
|
|
401
|
+
: undefined;
|
|
402
|
+
const output = process.env.SKILL_OUTPUT_TOKENS
|
|
403
|
+
? Number(process.env.SKILL_OUTPUT_TOKENS)
|
|
404
|
+
: undefined;
|
|
405
|
+
if (input == null && output == null)
|
|
406
|
+
return undefined;
|
|
407
|
+
return {
|
|
408
|
+
input_tokens: input,
|
|
409
|
+
output_tokens: output,
|
|
410
|
+
total_tokens: (input ?? 0) + (output ?? 0),
|
|
411
|
+
reported_by: "agent",
|
|
412
|
+
captured_at: new Date().toISOString(),
|
|
413
|
+
host: process.env.SKILL_HOST,
|
|
414
|
+
model: process.env.SKILL_MODEL,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
export async function discardSection(root, sectionId) {
|
|
418
|
+
const p = paths(root);
|
|
419
|
+
for (const dir of [p.sections, p.ingredientsLegacy]) {
|
|
420
|
+
const file = join(dir, `${sectionId}.json`);
|
|
421
|
+
if (existsSync(file))
|
|
422
|
+
await unlink(file);
|
|
423
|
+
}
|
|
424
|
+
await unstage(root, [sectionId]);
|
|
425
|
+
}
|
|
426
|
+
/** @deprecated */
|
|
427
|
+
export const discardIngredient = discardSection;
|
|
428
|
+
/** Load a continuity/release package for agent handoff resume. */
|
|
429
|
+
export async function loadSkillHandoff(packagePath) {
|
|
430
|
+
const { unpackSkill } = await import("@dot-skill/core");
|
|
431
|
+
const bytes = new Uint8Array(await readFile(resolve(packagePath)));
|
|
432
|
+
const u = unpackSkill(bytes);
|
|
433
|
+
const journey = u.raw.provenance?.journey;
|
|
434
|
+
return {
|
|
435
|
+
skill_id: u.manifest.id,
|
|
436
|
+
title: u.manifest.title,
|
|
437
|
+
intent: u.manifest.intent,
|
|
438
|
+
journey: u.raw.provenance?.journey,
|
|
439
|
+
generation_usage: u.raw.provenance?.generation_usage,
|
|
440
|
+
completeness: u.manifest.completeness,
|
|
441
|
+
compile_profile: u.manifest.compile_profile,
|
|
442
|
+
knowledge: u.knowledge.map((k) => ({ id: k.id, title: k.title, type: k.type })),
|
|
443
|
+
open_questions: journey?.open_questions,
|
|
444
|
+
mint_status: u.manifest.mint?.mint_status,
|
|
445
|
+
};
|
|
446
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dot-skill/workspace",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Git-like local .skill workspace — sections, stage, compile, checkpoint, mint",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Bharat Dudeja",
|
|
8
|
+
"url": "https://github.com/bharatdudeja13-cmd"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/dot-skill/dot-skill.git",
|
|
19
|
+
"directory": "packages/workspace"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
32
|
+
"prepack": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@dot-skill/core": "^0.4.1",
|
|
36
|
+
"@dot-skill/protocol": "^0.4.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.15.21",
|
|
40
|
+
"typescript": "^5.8.3"
|
|
41
|
+
}
|
|
42
|
+
}
|