@karmaniverous/stan-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,356 @@
1
+ /** Options to control archive creation. */
2
+ type CreateArchiveOptions = {
3
+ /** When true, include the `stanPath/output` directory inside the archive. */
4
+ includeOutputDir?: boolean;
5
+ /**
6
+ * Archive file name. If provided without `.tar`, the suffix is added.
7
+ * Written to `stanPath/output/<fileName>`.
8
+ */
9
+ fileName?: string;
10
+ /** Allow‑list globs; when provided, overrides excludes. */
11
+ includes?: string[];
12
+ /**
13
+ * Deny‑list globs. Defaults include `.git`, `node_modules`, and STAN
14
+ * workspace rules. These are applied only when `includes` is empty.
15
+ */
16
+ excludes?: string[];
17
+ /** Optional callback for archive classifier warnings (engine remains silent by default). */
18
+ onArchiveWarnings?: (text: string) => void;
19
+ };
20
+ /** Create `stanPath/output/archive.tar` (or custom file name) from the repo root. */
21
+ declare const createArchive: (cwd: string, stanPath: string, options?: CreateArchiveOptions) => Promise<string>;
22
+
23
+ /** Public default STAN path for consumers and internal use. */
24
+ declare const DEFAULT_STAN_PATH = ".stan";
25
+ /** Default command to open modified files after patch apply. */
26
+ declare const DEFAULT_OPEN_COMMAND = "code -g {file}";
27
+
28
+ /**
29
+ * Resolve the absolute path to the nearest `stan.config.*` starting from `cwd`.
30
+ *
31
+ * @param cwd - Directory to start searching from.
32
+ * @returns Absolute path to the config file, or `null` if none found.
33
+ */
34
+ declare const findConfigPathSync: (cwd: string) => string | null;
35
+
36
+ /** A script entry may be a raw command string or an object with an optional warnPattern. */
37
+ type ScriptEntry = string | {
38
+ script: string;
39
+ warnPattern?: string;
40
+ };
41
+ type ScriptMap = Record<string, ScriptEntry>;
42
+ type CliDefaultsRun = {
43
+ archive?: boolean;
44
+ combine?: boolean;
45
+ keep?: boolean;
46
+ sequential?: boolean;
47
+ live?: boolean;
48
+ plan?: boolean;
49
+ hangWarn?: number;
50
+ hangKill?: number;
51
+ hangKillGrace?: number;
52
+ scripts?: boolean | string[];
53
+ };
54
+ type CliDefaultsPatch = {
55
+ file?: string | null | undefined;
56
+ };
57
+ type CliDefaultsSnap = {
58
+ stash?: boolean;
59
+ };
60
+ type CliDefaults = {
61
+ debug?: boolean;
62
+ boring?: boolean;
63
+ patch?: CliDefaultsPatch;
64
+ run?: CliDefaultsRun;
65
+ snap?: CliDefaultsSnap;
66
+ };
67
+ /**
68
+ * Resolved STAN configuration. * - Paths like stanPath/output and stanPath/diff are referred to without angle * brackets to avoid confusion with HTML-like tags in TSDoc.
69
+ */
70
+ type ContextConfig = {
71
+ stanPath: string;
72
+ scripts: ScriptMap;
73
+ /**
74
+ * Additive allow‑list globs for archiving/snapshot logic.
75
+ * - Augments the base selection (which applies .gitignore and default denials).
76
+ * - Explicit `excludes` take precedence over `includes` (i.e., excludes always win).
77
+ * - `includes` can bring back files ignored by `.gitignore` or default denials.
78
+ * - Reserved exclusions still apply: `<stanPath>/diff` is always excluded;
79
+ * `<stanPath>/output` is excluded unless explicitly included by combine mode at archive time.
80
+ */
81
+ includes?: string[];
82
+ /** Paths to exclude in archiving logic (globs supported). */
83
+ excludes?: string[];
84
+ /** Maximum retained snapshot "undos" (history depth for snap undo/redo). */
85
+ maxUndos?: number /** Optional developer-mode switch to treat the current repo as the STAN dev module. */;
86
+ devMode?: boolean;
87
+ /**
88
+ * Staged imports (archiving only): label -\> glob(s) to copy under <stanPath>/imports/<label>/...
89
+ */
90
+ imports?: Record<string, string | string[]>;
91
+ /**
92
+ * Phase-scoped CLI defaults used by adapters when flags are omitted.
93
+ * Top-level (no 'opts' wrapper). */
94
+ cliDefaults?: CliDefaults;
95
+ /** Command template to open modified files after a successful patch. */
96
+ patchOpenCommand?: string;
97
+ };
98
+
99
+ /**
100
+ * Load and validate STAN configuration synchronously.
101
+ *
102
+ * @param cwd - Repo root or any descendant; the nearest `stan.config.*` is used.
103
+ * @returns Parsed, validated {@link ContextConfig}.
104
+ */
105
+ declare const loadConfigSync: (cwd: string) => ContextConfig;
106
+ /**
107
+ * Load and validate STAN configuration (async).
108
+ *
109
+ * @param cwd - Repo root or any descendant; the nearest `stan.config.*` is used.
110
+ * @returns Parsed, validated {@link ContextConfig}.
111
+ */
112
+ declare const loadConfig: (cwd: string) => Promise<ContextConfig>;
113
+ /** Resolve stanPath from config or fall back to default (sync). */
114
+ declare const resolveStanPathSync: (cwd: string) => string;
115
+ /** Resolve stanPath from config or fall back to default (async). */
116
+ declare const resolveStanPath: (cwd: string) => Promise<string>;
117
+
118
+ /**
119
+ * Ensure the STAN workspace exists and manage output/diff.
120
+ *
121
+ * Behavior:
122
+ * - Always ensure `stanPath/output` and `stanPath/diff` exist.
123
+ * - Also ensure `stanPath/patch` exists so archives can include it.
124
+ * - When `keep === false`, copy `output/archive.tar` to `diff/archive.prev.tar`
125
+ * if present, then clear only the `output` directory.
126
+ *
127
+ * @param cwd - Repo root.
128
+ * @param stanPath - Workspace folder (e.g., `.stan`).
129
+ * @param keep - When `true`, do not clear the output directory.
130
+ * @returns Absolute path to the workspace root (`stanPath`).
131
+ */
132
+ declare const ensureOutputDir: (cwd: string, stanPath: string, keep?: boolean) => Promise<string>;
133
+
134
+ type SnapshotUpdateMode = 'never' | 'createIfMissing' | 'replace';
135
+ /**
136
+ * Compute (and optionally update) the snapshot file in <stanPath>/diff/.
137
+ * Returns the absolute snapshot path.
138
+ *
139
+ * @param args - Object with:
140
+ * - cwd: Repo root.
141
+ * - stanPath: STAN workspace folder.
142
+ * - includes: Allow‑list globs (overrides excludes).
143
+ * - excludes: Deny‑list globs.
144
+ * @returns Absolute path to the `.archive.snapshot.json` file.
145
+ */
146
+ declare const writeArchiveSnapshot: ({ cwd, stanPath, includes, excludes, }: {
147
+ cwd: string;
148
+ stanPath: string;
149
+ includes?: string[];
150
+ excludes?: string[];
151
+ }) => Promise<string>;
152
+ /**
153
+ * Create a diff tar at <stanPath>/output/<baseName>.diff.tar.
154
+ * - If snapshot exists: include only changed files.
155
+ * - If no snapshot exists: include full file list (diff equals full archive).
156
+ * - Snapshot update behavior is controlled by updateSnapshot.
157
+ * - When includeOutputDirInDiff === true, also include the entire <stanPath>/output tree
158
+ * (excluding <stanPath>/diff and the two archive files) regardless of change list length.
159
+ * - Always include <stanPath>/patch in the diff archive.
160
+ *
161
+ * @param args - Object with:
162
+ * - cwd: Repo root.
163
+ * - stanPath: STAN workspace folder.
164
+ * - baseName: Base archive name (e.g., `archive` -\> `archive.diff.tar`).
165
+ * - includes: Allow‑list globs (overrides excludes).
166
+ * - excludes: Deny‑list globs.
167
+ * - updateSnapshot: Controls when the snapshot file is replaced.
168
+ * - includeOutputDirInDiff: When true, include `stanPath/output` in the diff.
169
+ * @returns `{ diffPath }` absolute path to the diff archive.
170
+ */
171
+ declare const createArchiveDiff: ({ cwd, stanPath, baseName, includes, excludes, updateSnapshot, includeOutputDirInDiff, onArchiveWarnings, }: {
172
+ cwd: string;
173
+ stanPath: string;
174
+ baseName: string;
175
+ includes?: string[];
176
+ excludes?: string[];
177
+ updateSnapshot?: SnapshotUpdateMode;
178
+ includeOutputDirInDiff?: boolean;
179
+ onArchiveWarnings?: (text: string) => void;
180
+ }) => Promise<{
181
+ diffPath: string;
182
+ }>;
183
+
184
+ /** src/stan/validate/response.ts
185
+ * Response-format validator for assistant replies.
186
+ *
187
+ * Also validates optional "### File Ops" pre-ops block (verbs/arity/path rules).
188
+ *
189
+ * Checks (initial):
190
+ * - One Patch per file.
191
+ * - Each Patch block contains exactly one "diff --git a/<path> b/<path>" header.
192
+ * - When both are present for a given file, "Patch" precedes "Full Listing".
193
+ * - "## Commit Message" exists and is the final section.
194
+ * - If any non‑docs Patch exists, there is also a Patch for ".stan/system/stan.todo.md".
195
+ */
196
+ /**
197
+ * Kind tag for validator blocks. Exported so it appears in generated
198
+ * documentation and to eliminate TypeDoc’s “referenced but not documented” warning.
199
+ */
200
+ type BlockKind = 'patch' | 'full' | 'commit';
201
+ type Block = {
202
+ kind: BlockKind;
203
+ /** Repo-relative target path for patch/listing blocks; undefined for commit. */ path?: string;
204
+ /** Start index (character offset) in the source for ordering checks. */
205
+ start: number;
206
+ /** Block body (content between its heading and the next heading). */
207
+ body: string;
208
+ };
209
+ type ValidationResult = {
210
+ ok: boolean;
211
+ errors: string[];
212
+ warnings: string[];
213
+ };
214
+ /** Validate an assistant reply body against response-format rules. */
215
+ declare const validateResponseMessage: (text: string) => ValidationResult;
216
+ /** Throw on validation failure (convenience API). */
217
+ declare const validateOrThrow: (text: string) => void;
218
+ declare const __internal: {
219
+ extractBlocks: (text: string) => Block[];
220
+ parseDiffHeaders: (body: string) => Array<{
221
+ a: string;
222
+ b: string;
223
+ }>;
224
+ isCommitLast: (text: string) => boolean;
225
+ };
226
+
227
+ /**
228
+ * Detect and clean a patch payload from clipboard/file/argument.
229
+ * - Unwraps chat fences and BEGIN/END PATCH banners when they wrap the entire payload.
230
+ * - Extracts the first unified diff (fenced or raw).
231
+ * - Normalizes EOL to LF, strips zero-width, and ensures a trailing newline.
232
+ */
233
+ declare const detectAndCleanPatch: (input: string) => string;
234
+
235
+ type FileOp = {
236
+ verb: 'mv';
237
+ src: string;
238
+ dest: string;
239
+ } | {
240
+ verb: 'rm';
241
+ src: string;
242
+ } | {
243
+ verb: 'rmdir';
244
+ src: string;
245
+ } | {
246
+ verb: 'mkdirp';
247
+ src: string;
248
+ };
249
+ type FileOpsPlan = {
250
+ ops: FileOp[];
251
+ errors: string[];
252
+ };
253
+ type OpResult = {
254
+ verb: FileOp['verb'];
255
+ src?: string;
256
+ dest?: string;
257
+ status: 'ok' | 'failed';
258
+ errno?: string;
259
+ message?: string;
260
+ };
261
+ /** Parse the optional "### File Ops" fenced block from a reply body. */
262
+ declare const parseFileOpsBlock: (source: string) => FileOpsPlan;
263
+ /** Execute File Ops with safety checks. Returns per-op results and overall ok. */
264
+ declare const executeFileOps: (cwd: string, ops: FileOp[], dryRun?: boolean) => Promise<{
265
+ ok: boolean;
266
+ results: OpResult[];
267
+ }>;
268
+
269
+ type AttemptCapture = {
270
+ label: string;
271
+ code: number;
272
+ stdout: string;
273
+ stderr: string;
274
+ };
275
+ type ApplyResult = {
276
+ ok: boolean;
277
+ tried: string[];
278
+ lastCode: number;
279
+ captures: AttemptCapture[];
280
+ };
281
+
282
+ type JsDiffOutcome = {
283
+ okFiles: string[];
284
+ failed: Array<{
285
+ path: string;
286
+ reason: string;
287
+ }>;
288
+ sandboxRoot?: string;
289
+ };
290
+ /** Apply cleaned unified diff text using jsdiff as a fallback engine. */
291
+ declare const applyWithJsDiff: (args: {
292
+ cwd: string;
293
+ cleaned: string;
294
+ check: boolean;
295
+ sandboxRoot?: string;
296
+ }) => Promise<JsDiffOutcome>;
297
+
298
+ type PipelineOutcome = {
299
+ ok: boolean;
300
+ result: ApplyResult;
301
+ js: JsDiffOutcome | null;
302
+ };
303
+ /** Apply a cleaned unified diff to the working tree (no staging). */
304
+ declare const applyPatchPipeline: (args: {
305
+ cwd: string;
306
+ patchAbs: string;
307
+ cleaned: string;
308
+ check: boolean;
309
+ /** Attempt order; defaults to [1,0] (p1 then p0). */
310
+ stripOrder?: number[];
311
+ }) => Promise<PipelineOutcome>;
312
+
313
+ type ImportsMap = Record<string, string[]>;
314
+ /**
315
+ * Prepare imports under <stanPath>/imports/<label>/... for archiving.
316
+ * - Cleans each label directory prior to staging.
317
+ * - Copies only files (skips directories); unreadable files are skipped best‑effort.
318
+ *
319
+ * @param args - Object with cwd, stanPath, and map of label -\> patterns.
320
+ * Optionally includes `onStage`, a callback invoked per label with
321
+ * repo‑relative staged paths.
322
+ */
323
+ declare const prepareImports: (args: {
324
+ cwd: string;
325
+ stanPath: string;
326
+ map?: ImportsMap | null;
327
+ onStage?: (label: string, files: string[]) => void;
328
+ }) => Promise<void>;
329
+
330
+ /** Resolve packaged dist/stan.system.md if present. */
331
+ declare const getPackagedSystemPromptPath: () => string | null;
332
+
333
+ type AssembleResult = {
334
+ target: string;
335
+ action: 'written';
336
+ } | {
337
+ target: string;
338
+ action: 'skipped-no-parts';
339
+ partsDir: string;
340
+ } | {
341
+ target: string;
342
+ action: 'skipped-no-md';
343
+ partsDir: string;
344
+ };
345
+ /**
346
+ * Assemble parts into the monolith (no logs).
347
+ * - Returns 'written' when created/updated,
348
+ * - 'skipped-no-parts' when parts dir missing,
349
+ * - 'skipped-no-md' when no .md files present.
350
+ */
351
+ declare const assembleSystemMonolith: (cwd: string, stanPath: string) => Promise<AssembleResult>;
352
+
353
+ declare const CORE_VERSION: string;
354
+
355
+ export { CORE_VERSION, DEFAULT_OPEN_COMMAND, DEFAULT_STAN_PATH, __internal, applyPatchPipeline, applyWithJsDiff, assembleSystemMonolith, createArchive, createArchiveDiff, detectAndCleanPatch, ensureOutputDir, executeFileOps, findConfigPathSync, getPackagedSystemPromptPath, loadConfig, loadConfigSync, parseFileOpsBlock, prepareImports, resolveStanPath, resolveStanPathSync, validateOrThrow, validateResponseMessage, writeArchiveSnapshot };
356
+ export type { ApplyResult, AssembleResult, Block, BlockKind, CliDefaults, CliDefaultsPatch, CliDefaultsRun, CliDefaultsSnap, ContextConfig, CreateArchiveOptions, FileOp, FileOpsPlan, ImportsMap, JsDiffOutcome, OpResult, PipelineOutcome, ScriptEntry, ScriptMap, SnapshotUpdateMode, ValidationResult };
package/package.json ADDED
@@ -0,0 +1,155 @@
1
+ {
2
+ "author": "Jason Williscroft",
3
+ "auto-changelog": {
4
+ "output": "CHANGELOG.md",
5
+ "unreleased": true,
6
+ "commitLimit": false,
7
+ "hideCredit": true
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/karmaniverous/stan-core/issues"
11
+ },
12
+ "dependencies": {
13
+ "@vitest/eslint-plugin": "^1.3.15",
14
+ "diff": "^8.0.2",
15
+ "fs-extra": "^11.3.2",
16
+ "fast-glob": "^3.3.3",
17
+ "glob-parent": "^6.0.2",
18
+ "ignore": "^7.0.5",
19
+ "package-directory": "^8.1.0",
20
+ "picomatch": "^4.0.3",
21
+ "zod": "^4.1.11",
22
+ "yaml": "^2.8.1"
23
+ },
24
+ "description": "Engine for STAN — programmatic archiving/diffing/snapshotting, patch application, config loading, selection, and imports staging. No CLI/TTY concerns.",
25
+ "devDependencies": {
26
+ "@dotenvx/dotenvx": "^1.51.0",
27
+ "@eslint/js": "^9.37.0",
28
+ "@rollup/plugin-alias": "^5.1.1",
29
+ "@rollup/plugin-commonjs": "^28.0.6",
30
+ "@rollup/plugin-json": "^6.1.0",
31
+ "@rollup/plugin-node-resolve": "^16.0.2",
32
+ "@rollup/plugin-terser": "^0.4.4",
33
+ "@rollup/plugin-typescript": "^12.1.4",
34
+ "@types/eslint-config-prettier": "^6.11.3",
35
+ "@types/fs-extra": "^11.0.4",
36
+ "@types/glob-parent": "^5.1.3",
37
+ "@types/node": "^24.6.2",
38
+ "@types/picomatch": "^4.0.2",
39
+ "@vitest/coverage-v8": "^3.2.4",
40
+ "auto-changelog": "^2.5.0",
41
+ "eslint": "^9.37.0",
42
+ "eslint-config-prettier": "^10.1.8",
43
+ "eslint-plugin-jsonc": "^2.21.0",
44
+ "eslint-plugin-prettier": "^5.5.4",
45
+ "eslint-plugin-simple-import-sort": "^12.1.1",
46
+ "eslint-plugin-tsdoc": "^0.4.0",
47
+ "happy-dom": "^19.0.2",
48
+ "jsonc-eslint-parser": "^2.4.1",
49
+ "knip": "^5.64.1",
50
+ "lefthook": "^1.13.6",
51
+ "prettier": "^3.6.2",
52
+ "release-it": "^19.0.5",
53
+ "rimraf": "^6.0.1",
54
+ "rollup": "^4.52.4",
55
+ "rollup-plugin-dts": "^6.2.3",
56
+ "tar": "^7.5.1",
57
+ "tslib": "^2.8.1",
58
+ "tsx": "^4.20.6",
59
+ "typedoc": "^0.28.13",
60
+ "typedoc-plugin-mdn-links": "^5.0.9",
61
+ "typedoc-plugin-replace-text": "^4.2.0",
62
+ "typescript": "^5.9.3",
63
+ "typescript-eslint": "^8.45.0",
64
+ "vitest": "^3.2.4"
65
+ },
66
+ "engines": {
67
+ "node": ">=20"
68
+ },
69
+ "exports": {
70
+ ".": {
71
+ "import": {
72
+ "types": "./dist/types/index.d.ts",
73
+ "default": "./dist/mjs/index.js"
74
+ },
75
+ "require": {
76
+ "types": "./dist/types/index.d.ts",
77
+ "default": "./dist/cjs/index.js"
78
+ }
79
+ }
80
+ },
81
+ "files": [
82
+ "dist"
83
+ ],
84
+ "keywords": [
85
+ "engine",
86
+ "library",
87
+ "ai-assisted-development",
88
+ "snapshot",
89
+ "archive",
90
+ "tar",
91
+ "unified-diff",
92
+ "patch",
93
+ "code-review",
94
+ "context",
95
+ "typescript"
96
+ ],
97
+ "license": "BSD-3-Clause",
98
+ "main": "dist/cjs/index.js",
99
+ "module": "dist/mjs/index.js",
100
+ "name": "@karmaniverous/stan-core",
101
+ "publishConfig": {
102
+ "access": "public"
103
+ },
104
+ "release-it": {
105
+ "git": {
106
+ "changelog": "npx auto-changelog --stdout --template https://raw.githubusercontent.com/release-it/release-it/main/templates/changelog-compact.hbs",
107
+ "commitMessage": "chore: release v${version}",
108
+ "requireBranch": "main"
109
+ },
110
+ "github": {
111
+ "release": true
112
+ },
113
+ "hooks": {
114
+ "after:init": [
115
+ "npm run lint",
116
+ "npm run test",
117
+ "npm run knip",
118
+ "npm run build"
119
+ ],
120
+ "before:npm:release": [
121
+ "npx auto-changelog -p",
122
+ "npm run docs",
123
+ "git add -A"
124
+ ],
125
+ "after:release": [
126
+ "git switch -c release/${version}",
127
+ "git push -u origin release/${version}",
128
+ "git switch ${branchName}"
129
+ ]
130
+ },
131
+ "npm": {
132
+ "publish": true
133
+ }
134
+ },
135
+ "repository": {
136
+ "type": "git",
137
+ "url": "git+https://github.com/karmaniverous/stan-core.git"
138
+ },
139
+ "scripts": {
140
+ "build": "tsx tools/gen-system.ts && rimraf dist .rollup.cache .tsbuildinfo && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
141
+ "changelog": "auto-changelog",
142
+ "docs": "typedoc",
143
+ "knip": "knip",
144
+ "lint": "eslint .",
145
+ "lint:fix": "npm run lint -- --fix .",
146
+ "release": "dotenvx run -f .env.local -- release-it",
147
+ "release:pre": "npm run release -- --no-git.requireBranch --github.prerelease --preRelease",
148
+ "relink": "npm uninstall -g @karmaniverous/stan-core && npm run build && npm link",
149
+ "test": "vitest --run --coverage --silent",
150
+ "typecheck": "tsc"
151
+ },
152
+ "type": "module",
153
+ "types": "dist/index.d.ts",
154
+ "version": "0.1.0"
155
+ }