@hobocode/thought-layer 0.7.0 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/core/artifacts-io.ts +146 -0
- package/core/artifacts.ts +690 -0
- package/core/index.ts +4 -0
- package/core/merge.ts +2 -1
- package/core/notion-io.ts +292 -0
- package/core/notion.ts +312 -0
- package/core/progress.ts +8 -5
- package/core/state-ops.ts +1 -1
- package/core/sync-io.ts +9 -6
- package/dist/tl.js +1298 -45
- package/extensions/thought-layer.ts +62 -2
- package/package.json +5 -1
- package/prompts/tl-artifacts.md +7 -0
- package/prompts/tl-compliance.md +7 -0
- package/prompts/tl-wiki.md +9 -0
- package/skills/thought-layer-compliance/SKILL.md +139 -0
- package/skills/thought-layer-wiki/SKILL.md +43 -0
package/core/progress.ts
CHANGED
|
@@ -21,10 +21,10 @@ export const PROGRESS_FORMAT = 2;
|
|
|
21
21
|
|
|
22
22
|
export const KNOWN_STATE_KEYS = [
|
|
23
23
|
"version", "answers", "feedback", "bizModel", "grill",
|
|
24
|
-
"assets", "research", "swot", "prd", "naming", "brand", "kit",
|
|
24
|
+
"assets", "research", "swot", "prd", "naming", "brand", "governance", "kit",
|
|
25
25
|
] as const;
|
|
26
26
|
|
|
27
|
-
export type ArtifactKey = "bizModel" | "grill" | "assets" | "research" | "swot" | "prd" | "naming" | "brand";
|
|
27
|
+
export type ArtifactKey = "bizModel" | "grill" | "assets" | "research" | "swot" | "prd" | "naming" | "brand" | "governance";
|
|
28
28
|
|
|
29
29
|
export interface Writer { kind: "web" | "kit"; version?: string; ts: number; }
|
|
30
30
|
|
|
@@ -81,6 +81,7 @@ export interface ProgressState {
|
|
|
81
81
|
prd: unknown;
|
|
82
82
|
naming: unknown;
|
|
83
83
|
brand: unknown;
|
|
84
|
+
governance: unknown;
|
|
84
85
|
kit: KitNamespace | null;
|
|
85
86
|
[extra: string]: unknown;
|
|
86
87
|
}
|
|
@@ -99,7 +100,7 @@ export interface ProgressPayload {
|
|
|
99
100
|
export function emptyState(): ProgressState {
|
|
100
101
|
return {
|
|
101
102
|
version: 2, answers: {}, feedback: {}, bizModel: null, grill: null,
|
|
102
|
-
assets: null, research: null, swot: null, prd: null, naming: null, brand: null, kit: null,
|
|
103
|
+
assets: null, research: null, swot: null, prd: null, naming: null, brand: null, governance: null, kit: null,
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -132,6 +133,7 @@ export function parseProgress(text: string): ProgressPayload {
|
|
|
132
133
|
prd: s["prd"] ?? null,
|
|
133
134
|
naming: s["naming"] ?? null,
|
|
134
135
|
brand: s["brand"] ?? null,
|
|
136
|
+
governance: s["governance"] ?? null,
|
|
135
137
|
kit: (s["kit"] as KitNamespace | undefined) ?? null,
|
|
136
138
|
};
|
|
137
139
|
return {
|
|
@@ -149,7 +151,7 @@ export function parseProgress(text: string): ProgressPayload {
|
|
|
149
151
|
export function buildProgress(state: Partial<ProgressState>, writer: Writer, exportedAt: string): ProgressPayload {
|
|
150
152
|
const s = (state || {}) as Record<string, unknown>;
|
|
151
153
|
const {
|
|
152
|
-
answers, feedback, bizModel, grill, assets, research, swot, prd, naming, brand, kit,
|
|
154
|
+
answers, feedback, bizModel, grill, assets, research, swot, prd, naming, brand, governance, kit,
|
|
153
155
|
version: _v, exportedAt: _ea, formatNewer: _fn, ...rest
|
|
154
156
|
} = s;
|
|
155
157
|
return {
|
|
@@ -170,6 +172,7 @@ export function buildProgress(state: Partial<ProgressState>, writer: Writer, exp
|
|
|
170
172
|
prd: prd ?? null,
|
|
171
173
|
naming: naming ?? null,
|
|
172
174
|
brand: brand ?? null,
|
|
175
|
+
governance: governance ?? null,
|
|
173
176
|
kit: (kit as KitNamespace | undefined) ?? null,
|
|
174
177
|
...rest,
|
|
175
178
|
},
|
|
@@ -299,7 +302,7 @@ export function summarizeState(state: ProgressState): StateSummary {
|
|
|
299
302
|
if (s === "green" || s === "yellow" || s === "red") byStatus[s]++;
|
|
300
303
|
else byStatus.ungraded++;
|
|
301
304
|
}
|
|
302
|
-
const artifacts = (["bizModel", "grill", "assets", "research", "swot", "prd", "naming", "brand"] as const)
|
|
305
|
+
const artifacts = (["bizModel", "grill", "assets", "research", "swot", "prd", "naming", "brand", "governance"] as const)
|
|
303
306
|
.filter((k) => state[k] != null);
|
|
304
307
|
const kit = (state.kit && typeof state.kit === "object") ? state.kit : null;
|
|
305
308
|
return {
|
package/core/state-ops.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type ArtifactKey, type PersonaInput, type EndState, type KitCursor,
|
|
13
13
|
} from "./progress.ts";
|
|
14
14
|
|
|
15
|
-
export const ARTIFACT_KEYS: ArtifactKey[] = ["bizModel", "grill", "assets", "research", "swot", "prd", "naming", "brand"];
|
|
15
|
+
export const ARTIFACT_KEYS: ArtifactKey[] = ["bizModel", "grill", "assets", "research", "swot", "prd", "naming", "brand", "governance"];
|
|
16
16
|
const END_STATES: EndState[] = ["open", "pass", "setAside"];
|
|
17
17
|
|
|
18
18
|
export interface StateOp {
|
package/core/sync-io.ts
CHANGED
|
@@ -60,7 +60,7 @@ export function ghAuthed(): boolean {
|
|
|
60
60
|
function syncConfigPath(): string {
|
|
61
61
|
return process.env["THOUGHT_LAYER_SYNC_CONFIG"] || join(homedir(), ".thought-layer", "sync.json");
|
|
62
62
|
}
|
|
63
|
-
function loadConfig(): SyncConfig {
|
|
63
|
+
export function loadConfig(): SyncConfig {
|
|
64
64
|
const p = syncConfigPath();
|
|
65
65
|
if (!existsSync(p)) return emptySyncConfig();
|
|
66
66
|
try { return parseSyncConfig(readFileSync(p, "utf8")); }
|
|
@@ -74,12 +74,14 @@ function saveConfig(cfg: SyncConfig): void {
|
|
|
74
74
|
|
|
75
75
|
// ---- git helpers -------------------------------------------------------------
|
|
76
76
|
|
|
77
|
-
interface Run { status: number; out: string; err: string; }
|
|
78
|
-
|
|
77
|
+
export interface Run { status: number; out: string; err: string; }
|
|
78
|
+
// Exported so the artifacts delivery layer (artifacts-io.ts) reuses the exact
|
|
79
|
+
// same git shell-out plumbing rather than re-implementing spawnSync.
|
|
80
|
+
export function git(dir: string | null, args: string[], timeout = 120000): Run {
|
|
79
81
|
const r = spawnSync("git", dir ? ["-C", dir, ...args] : args, { encoding: "utf8", timeout });
|
|
80
82
|
return { status: r.status ?? 1, out: r.stdout || "", err: r.stderr || "" };
|
|
81
83
|
}
|
|
82
|
-
function isGitRepo(dir: string): boolean {
|
|
84
|
+
export function isGitRepo(dir: string): boolean {
|
|
83
85
|
return existsSync(join(dir, ".git")) && git(dir, ["rev-parse", "--is-inside-work-tree"]).status === 0;
|
|
84
86
|
}
|
|
85
87
|
function dirNonEmpty(dir: string): boolean {
|
|
@@ -91,7 +93,7 @@ const absDir = (d: string, cwd = process.cwd()): string => (isAbsolute(d) ? d :
|
|
|
91
93
|
// ---- the clone scaffolding files written on init -----------------------------
|
|
92
94
|
|
|
93
95
|
const GITATTRIBUTES = `# The kit reconciles session JSON itself; never let git textually merge it\n# (which would corrupt the envelope). -merge keeps our copy in the working tree\n# on a conflict; the kit then rebuilds the merged result from the clean blobs.\n.thought-layer/*.json -merge\n`;
|
|
94
|
-
const GITIGNORE = `# A Thought Layer sessions repo holds session state only. Built product\n# artifacts and secrets never sync here.\nbuild.json\ndeploy.json\n*.local\n.env\n.env.*\n!.env.example\ndist/\n.netlify/\nnode_modules/\n`;
|
|
96
|
+
const GITIGNORE = `# A Thought Layer sessions repo holds session state only. Built product\n# artifacts and secrets never sync here.\nbuild.json\ndeploy.json\n*.local\n.env\n.env.*\n!.env.example\ndist/\n.netlify/\nnode_modules/\n# Delivered artifacts (tl artifacts) ARE intentionally tracked, even though the\n# same filenames are ignored elsewhere in the tree.\n!artifacts/\n!artifacts/**\n`;
|
|
95
97
|
const README = `# Thought Layer sessions\n\nThis private repo is the home for Thought Layer session files. Each session is one\nfile under \`.thought-layer/<name>.json\` (the portable validation and design state).\nUse the kit to work with them:\n\n tl sync open --name <session> pull and resume a session\n tl sync save --name <session> snapshot the current state, commit, and push\n tl sync list list the sessions in this repo\n\nCollaboration is handled by GitHub: add a collaborator to this repo in its GitHub\nsettings, and they can clone it and run the kit against the same sessions.\nThe kit reconciles concurrent edits itself (newest wins per field, conflicts are\nreported), so git never has to merge the JSON by hand.\n`;
|
|
96
98
|
|
|
97
99
|
function writeCloneScaffold(cloneDir: string): void {
|
|
@@ -136,7 +138,8 @@ export async function runSync(opts: SyncRunOptions, ctx: { ts: number; exportedA
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
// Resolve the workspace + clone dir an op targets (everything except init).
|
|
139
|
-
|
|
141
|
+
// Exported so artifacts-io.ts resolves the same sessions workspace.
|
|
142
|
+
export function resolveWorkspace(opts: SyncRunOptions, cfg: SyncConfig): { cloneDir: string; ws: SyncWorkspace | null } {
|
|
140
143
|
if (opts.dir && opts.dir.trim()) return { cloneDir: absDir(opts.dir), ws: null };
|
|
141
144
|
const env = process.env["THOUGHT_LAYER_SESSIONS_DIR"];
|
|
142
145
|
if (env && env.trim()) return { cloneDir: absDir(env), ws: null };
|