vigiles 2.0.0 → 2.2.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.
- package/README.md +182 -134
- package/dist/action-gate.d.ts +28 -0
- package/dist/action-gate.js +73 -0
- package/dist/cli.js +705 -230
- package/dist/community-skills.d.ts +22 -0
- package/dist/community-skills.js +86 -0
- package/dist/compile-generator.d.ts +48 -0
- package/dist/compile-generator.js +322 -0
- package/dist/compile.d.ts +4 -0
- package/dist/compile.js +239 -45
- package/dist/coverage.d.ts +56 -0
- package/dist/coverage.js +178 -0
- package/dist/doc-refs.d.ts +60 -0
- package/dist/doc-refs.js +246 -0
- package/dist/eval.d.ts +62 -0
- package/dist/eval.js +174 -0
- package/dist/evolve.js +60 -125
- package/dist/frontmatter.d.ts +75 -0
- package/dist/frontmatter.js +263 -0
- package/dist/generate-schema.d.ts +51 -0
- package/dist/generate-schema.js +125 -0
- package/dist/generate-types.js +39 -1
- package/dist/harness-test.d.ts +38 -0
- package/dist/harness-test.js +129 -0
- package/dist/hash.d.ts +8 -0
- package/dist/hash.js +16 -0
- package/dist/inline.d.ts +22 -4
- package/dist/inline.js +60 -13
- package/dist/integrity.d.ts +29 -0
- package/dist/integrity.js +44 -0
- package/dist/linters.d.ts +5 -3
- package/dist/linters.js +144 -4
- package/dist/mock-model.d.ts +31 -0
- package/dist/mock-model.js +189 -0
- package/dist/orphans.d.ts +52 -0
- package/dist/orphans.js +124 -0
- package/dist/proofs.js +22 -16
- package/dist/refs.d.ts +44 -0
- package/dist/refs.js +144 -0
- package/dist/session.d.ts +97 -0
- package/dist/session.js +306 -0
- package/dist/sidecar.d.ts +35 -0
- package/dist/sidecar.js +102 -0
- package/dist/skill-driver.d.ts +77 -0
- package/dist/skill-driver.js +76 -0
- package/dist/skill-runtime.d.ts +101 -0
- package/dist/skill-runtime.js +289 -0
- package/dist/skill-test.d.ts +47 -0
- package/dist/skill-test.js +77 -0
- package/dist/spec.d.ts +119 -13
- package/dist/spec.js +51 -4
- package/dist/symbols.d.ts +30 -0
- package/dist/symbols.js +142 -0
- package/dist/test-utils.d.ts +8 -0
- package/dist/test-utils.js +41 -0
- package/dist/types.d.ts +34 -8
- package/dist/types.js +19 -0
- package/dist/validate.js +5 -3
- package/package.json +26 -5
- package/.claude/settings.json +0 -46
- package/.claude/settings.local.json +0 -8
- package/.github/workflows/ci.yml +0 -81
- package/.prettierignore +0 -1
- package/.vigiles/generated.d.ts +0 -205
- package/CLAUDE.md +0 -95
- package/CLAUDE.md.spec.ts +0 -142
- package/CONTRIBUTING.md +0 -121
- package/dist/action.d.ts.map +0 -1
- package/dist/action.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cli.test.d.ts +0 -2
- package/dist/cli.test.d.ts.map +0 -1
- package/dist/cli.test.js +0 -650
- package/dist/cli.test.js.map +0 -1
- package/dist/compile.d.ts.map +0 -1
- package/dist/compile.js.map +0 -1
- package/dist/evolve.d.ts.map +0 -1
- package/dist/evolve.js.map +0 -1
- package/dist/freshness.d.ts +0 -67
- package/dist/freshness.d.ts.map +0 -1
- package/dist/freshness.js +0 -244
- package/dist/freshness.js.map +0 -1
- package/dist/freshness.test.d.ts +0 -2
- package/dist/freshness.test.d.ts.map +0 -1
- package/dist/freshness.test.js +0 -356
- package/dist/freshness.test.js.map +0 -1
- package/dist/generate-types.d.ts.map +0 -1
- package/dist/generate-types.js.map +0 -1
- package/dist/inline.d.ts.map +0 -1
- package/dist/inline.js.map +0 -1
- package/dist/inline.test.d.ts +0 -5
- package/dist/inline.test.d.ts.map +0 -1
- package/dist/inline.test.js +0 -152
- package/dist/inline.test.js.map +0 -1
- package/dist/linters.d.ts.map +0 -1
- package/dist/linters.js.map +0 -1
- package/dist/proofs.d.ts.map +0 -1
- package/dist/proofs.js.map +0 -1
- package/dist/proofs.test.d.ts +0 -9
- package/dist/proofs.test.d.ts.map +0 -1
- package/dist/proofs.test.js +0 -952
- package/dist/proofs.test.js.map +0 -1
- package/dist/spec.d.ts.map +0 -1
- package/dist/spec.js.map +0 -1
- package/dist/spec.test.d.ts +0 -2
- package/dist/spec.test.d.ts.map +0 -1
- package/dist/spec.test.js +0 -1222
- package/dist/spec.test.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/validate.test.d.ts +0 -2
- package/dist/validate.test.d.ts.map +0 -1
- package/dist/validate.test.js +0 -531
- package/dist/validate.test.js.map +0 -1
- package/docs/agent-setup.md +0 -85
- package/docs/agent-workflows.md +0 -103
- package/docs/comparison.md +0 -71
- package/docs/freshness.md +0 -124
- package/docs/inline-mode.md +0 -119
- package/docs/linter-support.md +0 -166
- package/docs/spec-format.md +0 -194
- package/eslint.config.mjs +0 -79
- package/examples/CLAUDE.md +0 -54
- package/examples/CLAUDE.md.spec.ts +0 -65
- package/examples/SKILL.md +0 -50
- package/examples/SKILL.md.spec.ts +0 -57
- package/fixtures/example-project/CLAUDE.md +0 -11
- package/fixtures/example-project/package.json +0 -9
- package/fixtures/example-project/src/index.ts +0 -3
- package/fixtures/example-project/src/utils.test.ts +0 -2
- package/fixtures/example-project/src/utils.ts +0 -3
- package/logo.png +0 -0
- package/research/adoption-strategy.md +0 -111
- package/research/agent-integration.md +0 -145
- package/research/ai-code-quality.md +0 -197
- package/research/code-search-for-agents.md +0 -313
- package/research/competitive-landscape.md +0 -163
- package/research/doc-freshness.md +0 -516
- package/research/executable-specs.md +0 -368
- package/research/feature-ideas.md +0 -464
- package/research/formal-proofs-for-agents.md +0 -338
- package/research/fp-for-agent-harness.md +0 -150
- package/research/fp-for-deterministic-ai.md +0 -131
- package/research/self-evolving-specs.md +0 -298
- package/schemas/claude-md-strict.yml +0 -18
- package/schemas/claude-md.yml +0 -6
- package/schemas/skill-strict.yml +0 -12
- package/schemas/skill.yml +0 -5
- package/skills/audit-feedback-loop/SKILL.md +0 -76
- package/skills/edit-spec/SKILL.md +0 -131
- package/skills/enforce-rules-format/SKILL.md +0 -71
- package/skills/generate-logo/SKILL.md +0 -102
- package/skills/generate-rule/SKILL.md +0 -90
- package/skills/linter-docs/clippy.md +0 -241
- package/skills/linter-docs/eslint.md +0 -384
- package/skills/linter-docs/pylint.md +0 -288
- package/skills/linter-docs/rubocop.md +0 -277
- package/skills/linter-docs/ruff.md +0 -187
- package/skills/linter-docs/stylelint.md +0 -247
- package/skills/migrate-to-spec/SKILL.md +0 -124
- package/skills/pr-to-lint-rule/SKILL.md +0 -97
- package/skills/strengthen/SKILL.md +0 -168
- package/src/action.ts +0 -214
- package/src/cli.test.ts +0 -914
- package/src/cli.ts +0 -1631
- package/src/compile.ts +0 -691
- package/src/evolve.ts +0 -781
- package/src/freshness.test.ts +0 -449
- package/src/freshness.ts +0 -299
- package/src/generate-types.ts +0 -448
- package/src/inline.test.ts +0 -206
- package/src/inline.ts +0 -164
- package/src/linters.ts +0 -739
- package/src/proofs.test.ts +0 -1314
- package/src/proofs.ts +0 -849
- package/src/spec.test.ts +0 -1471
- package/src/spec.ts +0 -427
- package/src/types.ts +0 -117
- package/src/validate.test.ts +0 -701
- package/src/validate.ts +0 -381
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vigiles — Skill testing: deterministic tests for a skill's action sequence.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the generator driver (`driveSkill`) for use inside an
|
|
5
|
+
* ordinary `node:test` / Vitest `test()` — no custom runner, no DSL. You script
|
|
6
|
+
* the *model* (the non-deterministic part) and assert the deterministic spine:
|
|
7
|
+
* which gates ran, in what order, which branch was taken, whether the result
|
|
8
|
+
* gate blocked. The model's prose quality is never asserted — that's the
|
|
9
|
+
* probabilistic boundary; everything else is deterministic and checkable.
|
|
10
|
+
*/
|
|
11
|
+
import { type SkillProgram, type GeneratorSkill } from "./skill-driver.js";
|
|
12
|
+
export type ModelFn = (prose: string) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Script the mocked model. Pass:
|
|
15
|
+
* - an array — answers consumed in order across all acts; or
|
|
16
|
+
* - a map keyed by a case-insensitive substring of the act's prose, where each
|
|
17
|
+
* value is a single answer, or an array consumed in order *per key* (so a
|
|
18
|
+
* loop's prompt can return different answers on successive iterations).
|
|
19
|
+
* Unmatched prose yields "".
|
|
20
|
+
*/
|
|
21
|
+
export declare function scriptModel(spec: readonly string[] | Readonly<Record<string, string | readonly string[]>>): ModelFn;
|
|
22
|
+
export interface SkillRunResult {
|
|
23
|
+
/** Every gate passed and the skill reached its end. */
|
|
24
|
+
readonly ok: boolean;
|
|
25
|
+
/** Trace index of the gate that blocked, or null when all passed. */
|
|
26
|
+
readonly blockedAt: number | null;
|
|
27
|
+
/** Prose steps the model executed, with the scripted answer fed back in. */
|
|
28
|
+
readonly acts: readonly {
|
|
29
|
+
prose: string;
|
|
30
|
+
answer: string;
|
|
31
|
+
}[];
|
|
32
|
+
/** Gates that ran, in order, with their label and outcome. */
|
|
33
|
+
readonly gates: readonly {
|
|
34
|
+
label: string;
|
|
35
|
+
terminal: boolean;
|
|
36
|
+
ok: boolean;
|
|
37
|
+
}[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Drive a skill to completion (or first failed gate) with a scripted model and
|
|
41
|
+
* return a friendly summary to assert on with plain `assert`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runSkill(skill: SkillProgram | GeneratorSkill, opts?: {
|
|
44
|
+
cwd?: string;
|
|
45
|
+
model?: ModelFn;
|
|
46
|
+
}): SkillRunResult;
|
|
47
|
+
//# sourceMappingURL=skill-test.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* vigiles — Skill testing: deterministic tests for a skill's action sequence.
|
|
4
|
+
*
|
|
5
|
+
* A thin wrapper over the generator driver (`driveSkill`) for use inside an
|
|
6
|
+
* ordinary `node:test` / Vitest `test()` — no custom runner, no DSL. You script
|
|
7
|
+
* the *model* (the non-deterministic part) and assert the deterministic spine:
|
|
8
|
+
* which gates ran, in what order, which branch was taken, whether the result
|
|
9
|
+
* gate blocked. The model's prose quality is never asserted — that's the
|
|
10
|
+
* probabilistic boundary; everything else is deterministic and checkable.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.scriptModel = scriptModel;
|
|
14
|
+
exports.runSkill = runSkill;
|
|
15
|
+
const skill_driver_js_1 = require("./skill-driver.js");
|
|
16
|
+
function gateLabel(g) {
|
|
17
|
+
if (g._ref === "cmd")
|
|
18
|
+
return g.command;
|
|
19
|
+
if (g._ref === "role")
|
|
20
|
+
return `project:${g.role}`;
|
|
21
|
+
return g.path;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Script the mocked model. Pass:
|
|
25
|
+
* - an array — answers consumed in order across all acts; or
|
|
26
|
+
* - a map keyed by a case-insensitive substring of the act's prose, where each
|
|
27
|
+
* value is a single answer, or an array consumed in order *per key* (so a
|
|
28
|
+
* loop's prompt can return different answers on successive iterations).
|
|
29
|
+
* Unmatched prose yields "".
|
|
30
|
+
*/
|
|
31
|
+
function scriptModel(spec) {
|
|
32
|
+
if (Array.isArray(spec)) {
|
|
33
|
+
const answers = spec;
|
|
34
|
+
let i = 0;
|
|
35
|
+
return () => answers[Math.min(i++, answers.length - 1)] ?? "";
|
|
36
|
+
}
|
|
37
|
+
const map = spec;
|
|
38
|
+
const idx = {};
|
|
39
|
+
return (prose) => {
|
|
40
|
+
const p = prose.toLowerCase();
|
|
41
|
+
for (const k of Object.keys(map)) {
|
|
42
|
+
if (!p.includes(k.toLowerCase()))
|
|
43
|
+
continue;
|
|
44
|
+
const v = map[k];
|
|
45
|
+
if (typeof v === "string")
|
|
46
|
+
return v;
|
|
47
|
+
const i = idx[k] ?? 0;
|
|
48
|
+
idx[k] = i + 1;
|
|
49
|
+
return v[Math.min(i, v.length - 1)] ?? "";
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Drive a skill to completion (or first failed gate) with a scripted model and
|
|
56
|
+
* return a friendly summary to assert on with plain `assert`.
|
|
57
|
+
*/
|
|
58
|
+
function runSkill(skill, opts = {}) {
|
|
59
|
+
const program = typeof skill === "function" ? skill : skill.program;
|
|
60
|
+
const report = (0, skill_driver_js_1.driveSkill)(program, opts.cwd ?? process.cwd(), opts.model ?? (() => ""));
|
|
61
|
+
const acts = [];
|
|
62
|
+
const gates = [];
|
|
63
|
+
for (const t of report.trace) {
|
|
64
|
+
if (t.effect.kind === "act") {
|
|
65
|
+
acts.push({ prose: t.effect.prose, answer: t.answer ?? "" });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
gates.push({
|
|
69
|
+
label: gateLabel(t.effect.gate),
|
|
70
|
+
terminal: t.effect.kind === "result",
|
|
71
|
+
ok: t.outcome?.ok ?? false,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { ok: report.ok, blockedAt: report.blockedAt, acts, gates };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=skill-test.js.map
|
package/dist/spec.d.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* enforce() — delegated to an external linter (ESLint, Ruff, Clippy, etc.)
|
|
9
9
|
* guidance() — prose only, no mechanical enforcement
|
|
10
10
|
*/
|
|
11
|
-
/** Linters vigiles can cross-reference. */
|
|
12
|
-
type BuiltinLinter = "eslint" | "stylelint" | "ruff" | "clippy" | "pylint" | "rubocop";
|
|
11
|
+
/** Linters and policy catalogs vigiles can cross-reference. */
|
|
12
|
+
type BuiltinLinter = "eslint" | "stylelint" | "ruff" | "clippy" | "pylint" | "rubocop" | "cedar";
|
|
13
13
|
/** Scoped ESLint plugin prefix (e.g., @typescript-eslint). */
|
|
14
14
|
type ScopedPlugin = `@${string}/${string}`;
|
|
15
15
|
/** A linter/rule reference: "eslint/no-console", "ruff/T201", "@typescript-eslint/no-explicit-any". */
|
|
@@ -52,12 +52,12 @@ export type StrictFile = [keyof KnownProjectFiles] extends [never] ? string : Kn
|
|
|
52
52
|
export type StrictCmd = [keyof KnownNpmScripts] extends [never] ? string : `npm run ${KnownNpmScripts[keyof KnownNpmScripts] & string}` | `npm ${KnownNpmScripts[keyof KnownNpmScripts] & string}` | (string & {});
|
|
53
53
|
export type ClaudeTool = "Read" | "Write" | "Edit" | "Bash" | "Grep" | "Glob" | "Agent" | "TodoWrite" | "WebSearch" | "WebFetch" | "NotebookEdit";
|
|
54
54
|
export type HookEvent = "PreToolUse" | "PostToolUse" | "PreSession" | "PostSession" | "Notification";
|
|
55
|
-
/** A rule delegated to an external tool (linter, ast-grep, dependency-cruiser, etc.). */
|
|
55
|
+
/** A rule delegated to an external tool (linter, ast-grep, dependency-cruiser, etc.) or to a vigiles-internal check. */
|
|
56
56
|
export interface EnforceRule {
|
|
57
57
|
readonly _kind: "enforce";
|
|
58
|
-
readonly linterRule: LinterRule;
|
|
58
|
+
readonly linterRule: LinterRule | VigilesRef;
|
|
59
59
|
readonly why: string;
|
|
60
|
-
/** Skip
|
|
60
|
+
/** Skip verification for this rule. Default: true (verify). */
|
|
61
61
|
readonly verify: boolean;
|
|
62
62
|
}
|
|
63
63
|
/** A guidance-only rule (prose, no enforcement). */
|
|
@@ -65,18 +65,28 @@ export interface GuidanceRule {
|
|
|
65
65
|
readonly _kind: "guidance";
|
|
66
66
|
readonly text: string;
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
/** A reactive rule: runs a command when watched files change. */
|
|
69
|
+
export interface GuardRule {
|
|
70
|
+
readonly _kind: "guard";
|
|
71
|
+
readonly watch: string | readonly string[];
|
|
72
|
+
readonly run: string;
|
|
73
|
+
readonly description: string;
|
|
74
|
+
}
|
|
75
|
+
export type Rule = EnforceRule | GuidanceRule | GuardRule;
|
|
69
76
|
/**
|
|
70
|
-
* Declare a rule enforced by an external tool.
|
|
77
|
+
* Declare a rule enforced by an external tool or vigiles itself.
|
|
71
78
|
*
|
|
72
79
|
* When generated types are present, the `linterRule` argument is narrowed
|
|
73
|
-
* to only accept rules that exist in your linter configs.
|
|
80
|
+
* to only accept rules that exist in your linter configs. Vigiles-internal
|
|
81
|
+
* checks use the `vigiles/<assertion-id>` namespace and are dispatched to
|
|
82
|
+
* built-in mechanical validators (e.g. `vigiles/orphan-docs`).
|
|
74
83
|
*
|
|
75
84
|
* enforce("eslint/no-console", "Use structured logger.")
|
|
76
85
|
* enforce("@typescript-eslint/no-floating-promises", "Always await.")
|
|
77
86
|
* enforce("ruff/T201", "Use logging module.")
|
|
87
|
+
* enforce("vigiles/orphan-docs", "No docs without spec references.")
|
|
78
88
|
*/
|
|
79
|
-
export declare function enforce(
|
|
89
|
+
export declare function enforce(ref: NoInfer<StrictLinterRule> | VigilesRef, why: string, options?: {
|
|
80
90
|
verify?: boolean;
|
|
81
91
|
}): EnforceRule;
|
|
82
92
|
/**
|
|
@@ -85,6 +95,16 @@ export declare function enforce(linterRule: NoInfer<StrictLinterRule>, why: stri
|
|
|
85
95
|
* guidance("Google unfamiliar APIs before implementing.")
|
|
86
96
|
*/
|
|
87
97
|
export declare function guidance(text: string): GuidanceRule;
|
|
98
|
+
/**
|
|
99
|
+
* Declare a reactive guard: runs a command when watched files change.
|
|
100
|
+
*
|
|
101
|
+
* guard({ watch: "*.spec.ts", run: "npx vigiles compile" }, "Recompile on spec change")
|
|
102
|
+
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate-types" }, "Regen types")
|
|
103
|
+
*/
|
|
104
|
+
export declare function guard(options: {
|
|
105
|
+
watch: string | readonly string[];
|
|
106
|
+
run: string;
|
|
107
|
+
}, description: string): GuardRule;
|
|
88
108
|
/**
|
|
89
109
|
* Branded string types — these prove a reference has gone through
|
|
90
110
|
* vigiles's verification. The compiler only accepts branded refs,
|
|
@@ -115,7 +135,13 @@ export interface SkillRef {
|
|
|
115
135
|
readonly _ref: "skill";
|
|
116
136
|
readonly path: VerifiedRef;
|
|
117
137
|
}
|
|
118
|
-
|
|
138
|
+
/** A typed symbol reference — the named file must define the named symbol. */
|
|
139
|
+
export interface SymbolRef {
|
|
140
|
+
readonly _ref: "symbol";
|
|
141
|
+
readonly file: VerifiedPath;
|
|
142
|
+
readonly symbol: string;
|
|
143
|
+
}
|
|
144
|
+
export type Ref = FileRef | CmdRef | SkillRef | SymbolRef;
|
|
119
145
|
/**
|
|
120
146
|
* Reference a file path — verified to exist at compile time.
|
|
121
147
|
* When generated types are present, narrowed to known project files.
|
|
@@ -126,6 +152,13 @@ export declare function file(path: NoInfer<StrictFile>): FileRef;
|
|
|
126
152
|
* When generated types are present, narrowed to known npm scripts.
|
|
127
153
|
*/
|
|
128
154
|
export declare function cmd(command: NoInfer<StrictCmd>): CmdRef;
|
|
155
|
+
/**
|
|
156
|
+
* Reference a symbol defined in a file — verified at compile time that the
|
|
157
|
+
* named file exists AND defines the named symbol (via ast-grep, cross-language).
|
|
158
|
+
* Compiles to the file-qualified inline form `` `file#symbol` `` so the markdown
|
|
159
|
+
* `audit` / `refs-hook` re-verify the same reference.
|
|
160
|
+
*/
|
|
161
|
+
export declare function symbol(file: NoInfer<StrictFile>, name: string): SymbolRef;
|
|
129
162
|
/**
|
|
130
163
|
* Reference another skill or instruction file — verified to exist.
|
|
131
164
|
* Compiles to a markdown link: [skill name](path)
|
|
@@ -194,18 +227,91 @@ type ClaudeSpecInput = ClaudeSpecBase & ClaudeSpecSections;
|
|
|
194
227
|
* export default claude({ commands: {...}, rules: {...} });
|
|
195
228
|
*/
|
|
196
229
|
export declare function claude(spec: ClaudeSpecInput): ClaudeSpec;
|
|
230
|
+
/**
|
|
231
|
+
* A deterministic gate on a skill step or its final result. A gate is one of:
|
|
232
|
+
* a command (exit 0), a file (must exist), or a *project role* that resolves to
|
|
233
|
+
* the host project's real command at run time. cmd/file gates are verified
|
|
234
|
+
* against the repo at author time; role gates are portable — a skill that runs
|
|
235
|
+
* in other repos should prefer `project("test")` over a hard-coded `npm test`.
|
|
236
|
+
*/
|
|
237
|
+
export type Gate = CmdRef | FileRef | RoleGate;
|
|
238
|
+
/** Project command roles, resolved per host project at run time. */
|
|
239
|
+
export type ProjectRole = "test" | "build" | "lint";
|
|
240
|
+
/** A portable gate that resolves to the host project's command for a role. */
|
|
241
|
+
export interface RoleGate {
|
|
242
|
+
readonly _ref: "role";
|
|
243
|
+
readonly role: ProjectRole;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* A portable gate that resolves to the host project's command for a role
|
|
247
|
+
* (e.g. `project("test")` → `npm test` / `pytest` / `cargo test`). Use this
|
|
248
|
+
* in skills meant to run across projects, instead of hard-coding a command.
|
|
249
|
+
*/
|
|
250
|
+
export declare function project(role: ProjectRole): RoleGate;
|
|
251
|
+
/**
|
|
252
|
+
* A declared skill input. Compiles to the `argument-hint` frontmatter and a
|
|
253
|
+
* `## Arguments` section; referenced as `$1`/`$2`/`$ARGUMENTS` in the body.
|
|
254
|
+
*/
|
|
255
|
+
export interface SkillInput {
|
|
256
|
+
/** Argument name, e.g. "pattern". */
|
|
257
|
+
readonly name: string;
|
|
258
|
+
/** Human-readable hint shown in argument-hint and the Arguments section. */
|
|
259
|
+
readonly hint: string;
|
|
260
|
+
/** Required by default; set false to render as optional (`[<name>]`). */
|
|
261
|
+
readonly required?: boolean;
|
|
262
|
+
}
|
|
263
|
+
/** One step of a gated skill pipeline. */
|
|
264
|
+
export interface SkillStep {
|
|
265
|
+
/** What the model should do — prose, optionally with typed refs. */
|
|
266
|
+
readonly do: string | InstructionFragment[];
|
|
267
|
+
/** Deterministic check that must pass before advancing to the next step. */
|
|
268
|
+
readonly gate?: Gate;
|
|
269
|
+
/** Max attempts to satisfy the gate before the step fails (default 1). */
|
|
270
|
+
readonly retry?: number;
|
|
271
|
+
}
|
|
272
|
+
/** Declare a skill input (compiles to argument-hint + an Arguments entry). */
|
|
273
|
+
export declare function input(name: string, hint: string, opts?: {
|
|
274
|
+
required?: boolean;
|
|
275
|
+
}): SkillInput;
|
|
276
|
+
/** Declare a gated pipeline step. */
|
|
277
|
+
export declare function step(instr: string | InstructionFragment[], opts?: {
|
|
278
|
+
gate?: Gate;
|
|
279
|
+
retry?: number;
|
|
280
|
+
}): SkillStep;
|
|
197
281
|
export interface SkillSpec {
|
|
198
282
|
readonly _specType: "skill";
|
|
199
283
|
/** Skill name (used in frontmatter). */
|
|
200
284
|
readonly name: string;
|
|
201
285
|
/** Short description (used in frontmatter). */
|
|
202
286
|
readonly description: string;
|
|
203
|
-
/**
|
|
287
|
+
/**
|
|
288
|
+
* Hint for the argument (frontmatter). Ignored when `inputs` is set —
|
|
289
|
+
* `inputs` derive the argument-hint instead.
|
|
290
|
+
*/
|
|
204
291
|
readonly argumentHint?: string;
|
|
292
|
+
/** Typed inputs — compile to argument-hint + a `## Arguments` section. */
|
|
293
|
+
readonly inputs?: readonly SkillInput[];
|
|
205
294
|
/** Whether to disable model invocation (frontmatter flag). */
|
|
206
295
|
readonly disableModelInvocation?: boolean;
|
|
207
|
-
/**
|
|
208
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Gated pipeline steps. When set, the skill compiles to a `## Steps`
|
|
298
|
+
* checklist with a deterministic gate per step. Use this OR `body`.
|
|
299
|
+
*/
|
|
300
|
+
readonly steps?: readonly SkillStep[];
|
|
301
|
+
/**
|
|
302
|
+
* Terminal postcondition — the skill is "done" only when this gate passes.
|
|
303
|
+
* Compiles to a `## Result` section + a `vigiles:result` marker.
|
|
304
|
+
*/
|
|
305
|
+
readonly result?: Gate;
|
|
306
|
+
/** Freeform instruction body (linear/unstructured skills). Use this OR `steps`. */
|
|
307
|
+
readonly body?: string | InstructionFragment[];
|
|
308
|
+
/**
|
|
309
|
+
* Max lines for an inline fenced code block before compilation errors,
|
|
310
|
+
* forcing the script into a file referenced via `file()` (default 20).
|
|
311
|
+
* Keeps big scripts out of the skill body (token budget + progressive
|
|
312
|
+
* disclosure). Set 0 to disable.
|
|
313
|
+
*/
|
|
314
|
+
readonly maxInlineCodeLines?: number;
|
|
209
315
|
}
|
|
210
316
|
/**
|
|
211
317
|
* Define a SKILL.md specification.
|
package/dist/spec.js
CHANGED
|
@@ -12,31 +12,39 @@
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.enforce = enforce;
|
|
14
14
|
exports.guidance = guidance;
|
|
15
|
+
exports.guard = guard;
|
|
15
16
|
exports.file = file;
|
|
16
17
|
exports.cmd = cmd;
|
|
18
|
+
exports.symbol = symbol;
|
|
17
19
|
exports.ref = ref;
|
|
18
20
|
exports.instructions = instructions;
|
|
19
21
|
exports.claude = claude;
|
|
22
|
+
exports.project = project;
|
|
23
|
+
exports.input = input;
|
|
24
|
+
exports.step = step;
|
|
20
25
|
exports.skill = skill;
|
|
21
26
|
exports.defineConfig = defineConfig;
|
|
22
27
|
// ---------------------------------------------------------------------------
|
|
23
28
|
// Builder functions
|
|
24
29
|
// ---------------------------------------------------------------------------
|
|
25
30
|
/**
|
|
26
|
-
* Declare a rule enforced by an external tool.
|
|
31
|
+
* Declare a rule enforced by an external tool or vigiles itself.
|
|
27
32
|
*
|
|
28
33
|
* When generated types are present, the `linterRule` argument is narrowed
|
|
29
|
-
* to only accept rules that exist in your linter configs.
|
|
34
|
+
* to only accept rules that exist in your linter configs. Vigiles-internal
|
|
35
|
+
* checks use the `vigiles/<assertion-id>` namespace and are dispatched to
|
|
36
|
+
* built-in mechanical validators (e.g. `vigiles/orphan-docs`).
|
|
30
37
|
*
|
|
31
38
|
* enforce("eslint/no-console", "Use structured logger.")
|
|
32
39
|
* enforce("@typescript-eslint/no-floating-promises", "Always await.")
|
|
33
40
|
* enforce("ruff/T201", "Use logging module.")
|
|
41
|
+
* enforce("vigiles/orphan-docs", "No docs without spec references.")
|
|
34
42
|
*/
|
|
35
|
-
function enforce(
|
|
43
|
+
function enforce(ref, why, options) {
|
|
36
44
|
return {
|
|
37
45
|
_kind: "enforce",
|
|
38
46
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
39
|
-
linterRule:
|
|
47
|
+
linterRule: ref,
|
|
40
48
|
why,
|
|
41
49
|
verify: options?.verify ?? true,
|
|
42
50
|
};
|
|
@@ -49,6 +57,20 @@ function enforce(linterRule, why, options) {
|
|
|
49
57
|
function guidance(text) {
|
|
50
58
|
return { _kind: "guidance", text };
|
|
51
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Declare a reactive guard: runs a command when watched files change.
|
|
62
|
+
*
|
|
63
|
+
* guard({ watch: "*.spec.ts", run: "npx vigiles compile" }, "Recompile on spec change")
|
|
64
|
+
* guard({ watch: ["eslint.config.*", "package.json"], run: "npx vigiles generate-types" }, "Regen types")
|
|
65
|
+
*/
|
|
66
|
+
function guard(options, description) {
|
|
67
|
+
return {
|
|
68
|
+
_kind: "guard",
|
|
69
|
+
watch: options.watch,
|
|
70
|
+
run: options.run,
|
|
71
|
+
description,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
52
74
|
/**
|
|
53
75
|
* Reference a file path — verified to exist at compile time.
|
|
54
76
|
* When generated types are present, narrowed to known project files.
|
|
@@ -63,6 +85,15 @@ function file(path) {
|
|
|
63
85
|
function cmd(command) {
|
|
64
86
|
return { _ref: "cmd", command: command };
|
|
65
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Reference a symbol defined in a file — verified at compile time that the
|
|
90
|
+
* named file exists AND defines the named symbol (via ast-grep, cross-language).
|
|
91
|
+
* Compiles to the file-qualified inline form `` `file#symbol` `` so the markdown
|
|
92
|
+
* `audit` / `refs-hook` re-verify the same reference.
|
|
93
|
+
*/
|
|
94
|
+
function symbol(file, name) {
|
|
95
|
+
return { _ref: "symbol", file: file, symbol: name };
|
|
96
|
+
}
|
|
66
97
|
/**
|
|
67
98
|
* Reference another skill or instruction file — verified to exist.
|
|
68
99
|
* Compiles to a markdown link: [skill name](path)
|
|
@@ -98,6 +129,22 @@ function instructions(strings, ...values) {
|
|
|
98
129
|
function claude(spec) {
|
|
99
130
|
return { _specType: "claude", ...spec };
|
|
100
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* A portable gate that resolves to the host project's command for a role
|
|
134
|
+
* (e.g. `project("test")` → `npm test` / `pytest` / `cargo test`). Use this
|
|
135
|
+
* in skills meant to run across projects, instead of hard-coding a command.
|
|
136
|
+
*/
|
|
137
|
+
function project(role) {
|
|
138
|
+
return { _ref: "role", role };
|
|
139
|
+
}
|
|
140
|
+
/** Declare a skill input (compiles to argument-hint + an Arguments entry). */
|
|
141
|
+
function input(name, hint, opts = {}) {
|
|
142
|
+
return { name, hint, required: opts.required };
|
|
143
|
+
}
|
|
144
|
+
/** Declare a gated pipeline step. */
|
|
145
|
+
function step(instr, opts = {}) {
|
|
146
|
+
return { do: instr, gate: opts.gate, retry: opts.retry };
|
|
147
|
+
}
|
|
101
148
|
/**
|
|
102
149
|
* Define a SKILL.md specification.
|
|
103
150
|
*
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Lang } from "@ast-grep/napi";
|
|
2
|
+
/** A language key accepted by ast-grep's `parse` (core enum or registered id). */
|
|
3
|
+
type LangKey = Lang | string;
|
|
4
|
+
/** The ast-grep language for a file, or null if unsupported (graceful skip). */
|
|
5
|
+
export declare function langForFile(file: string): LangKey | null;
|
|
6
|
+
/** A symbol definition found in a file. */
|
|
7
|
+
export interface SymbolDef {
|
|
8
|
+
/** The defined identifier, e.g. "parseConfig". */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
/** The tree-sitter node kind, e.g. "function_declaration" (raw, per-grammar). */
|
|
11
|
+
readonly kind: string;
|
|
12
|
+
/** Enclosing class/module name, or "" at top level. */
|
|
13
|
+
readonly scope: string;
|
|
14
|
+
/** 1-based line of the definition. */
|
|
15
|
+
readonly line: number;
|
|
16
|
+
}
|
|
17
|
+
/** Extract the symbols defined in a single file's source. */
|
|
18
|
+
export declare function definedSymbols(code: string, lang: LangKey): SymbolDef[];
|
|
19
|
+
/** Defined symbols for a file on disk, or [] if unreadable/unsupported. */
|
|
20
|
+
export declare function definedSymbolsInFile(file: string): SymbolDef[];
|
|
21
|
+
/**
|
|
22
|
+
* Whether `file` defines a top-level (or scoped) symbol named `name`. This is
|
|
23
|
+
* the whole check for a file-qualified reference (`path#symbol`): we parse the
|
|
24
|
+
* one named file — no project-wide index, no resolution across files. As a
|
|
25
|
+
* fallback we also consult a co-located declaration file (`.rbi` / `.d.ts`), so
|
|
26
|
+
* typed dynamic symbols resolve without running Sorbet / the TS compiler.
|
|
27
|
+
*/
|
|
28
|
+
export declare function fileDefinesSymbol(file: string, name: string): boolean;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=symbols.d.ts.map
|
package/dist/symbols.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.langForFile = langForFile;
|
|
7
|
+
exports.definedSymbols = definedSymbols;
|
|
8
|
+
exports.definedSymbolsInFile = definedSymbolsInFile;
|
|
9
|
+
exports.fileDefinesSymbol = fileDefinesSymbol;
|
|
10
|
+
/**
|
|
11
|
+
* vigiles — cross-language symbol index (ast-grep / tree-sitter).
|
|
12
|
+
*
|
|
13
|
+
* The kernel of harness-pinned reference verification. Instruction files
|
|
14
|
+
* reference project symbols in prose (`parseConfig`, `User`, `create_docx.py`);
|
|
15
|
+
* authors never write a verifiable `file#symbol` form (corpus: ~0%). So instead
|
|
16
|
+
* of asking authors to annotate, the *harness* resolves a bare reference against
|
|
17
|
+
* the live code at write time and pins it. This module is the resolver: extract
|
|
18
|
+
* the symbols a file defines, and build a project-wide name → locations index.
|
|
19
|
+
*
|
|
20
|
+
* Boundary (see research/symbol-verification.md): we answer "does a definition
|
|
21
|
+
* with this name (in this scope) exist", NOT "does this reference resolve through
|
|
22
|
+
* imports / Zeitwerk / tsconfig". Resolution is per-language and architectural —
|
|
23
|
+
* delegated. Ambiguity (a name defined in several files) is reported, not guessed.
|
|
24
|
+
*/
|
|
25
|
+
const node_fs_1 = require("node:fs");
|
|
26
|
+
const node_path_1 = require("node:path");
|
|
27
|
+
const napi_1 = require("@ast-grep/napi");
|
|
28
|
+
const lang_python_1 = __importDefault(require("@ast-grep/lang-python"));
|
|
29
|
+
const lang_rust_1 = __importDefault(require("@ast-grep/lang-rust"));
|
|
30
|
+
const lang_ruby_1 = __importDefault(require("@ast-grep/lang-ruby"));
|
|
31
|
+
let registered = false;
|
|
32
|
+
function ensureRegistered() {
|
|
33
|
+
if (registered)
|
|
34
|
+
return;
|
|
35
|
+
// Non-web grammars ship as separate packages registered at runtime.
|
|
36
|
+
(0, napi_1.registerDynamicLanguage)({ python: lang_python_1.default, rust: lang_rust_1.default, ruby: lang_ruby_1.default });
|
|
37
|
+
registered = true;
|
|
38
|
+
}
|
|
39
|
+
const EXT_LANG = {
|
|
40
|
+
".ts": napi_1.Lang.TypeScript,
|
|
41
|
+
".tsx": napi_1.Lang.Tsx,
|
|
42
|
+
".mts": napi_1.Lang.TypeScript,
|
|
43
|
+
".cts": napi_1.Lang.TypeScript,
|
|
44
|
+
".d.ts": napi_1.Lang.TypeScript,
|
|
45
|
+
".js": napi_1.Lang.JavaScript,
|
|
46
|
+
".jsx": napi_1.Lang.JavaScript,
|
|
47
|
+
".mjs": napi_1.Lang.JavaScript,
|
|
48
|
+
".cjs": napi_1.Lang.JavaScript,
|
|
49
|
+
".css": napi_1.Lang.Css,
|
|
50
|
+
".py": "python",
|
|
51
|
+
".pyi": "python",
|
|
52
|
+
".rs": "rust",
|
|
53
|
+
".rb": "ruby",
|
|
54
|
+
".rbi": "ruby",
|
|
55
|
+
};
|
|
56
|
+
/** The ast-grep language for a file, or null if unsupported (graceful skip). */
|
|
57
|
+
function langForFile(file) {
|
|
58
|
+
if (file.endsWith(".d.ts"))
|
|
59
|
+
return napi_1.Lang.TypeScript;
|
|
60
|
+
return EXT_LANG[(0, node_path_1.extname)(file).toLowerCase()] ?? null;
|
|
61
|
+
}
|
|
62
|
+
const ID_KINDS = new Set(["identifier", "constant", "type_identifier"]);
|
|
63
|
+
const SCOPE_KINDS = new Set([
|
|
64
|
+
"class_declaration",
|
|
65
|
+
"class_definition",
|
|
66
|
+
"class",
|
|
67
|
+
"module",
|
|
68
|
+
"interface_declaration",
|
|
69
|
+
"enum_declaration",
|
|
70
|
+
]);
|
|
71
|
+
function recordNode(node, scope, out) {
|
|
72
|
+
const line = node.range().start.line + 1;
|
|
73
|
+
const nameNode = node.field("name");
|
|
74
|
+
if (nameNode) {
|
|
75
|
+
out.push({ name: nameNode.text(), kind: node.kind(), scope, line });
|
|
76
|
+
}
|
|
77
|
+
// Assignment-style constants (Python/Ruby `X = ...`): the identifier is the
|
|
78
|
+
// `left` field, not `name`.
|
|
79
|
+
const left = node.field("left");
|
|
80
|
+
if (left && ID_KINDS.has(left.kind())) {
|
|
81
|
+
out.push({ name: left.text(), kind: node.kind(), scope, line });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Extract the symbols defined in a single file's source. */
|
|
85
|
+
function definedSymbols(code, lang) {
|
|
86
|
+
ensureRegistered();
|
|
87
|
+
const out = [];
|
|
88
|
+
const walk = (node, scope) => {
|
|
89
|
+
recordNode(node, scope, out);
|
|
90
|
+
const nameNode = node.field("name");
|
|
91
|
+
const nextScope = SCOPE_KINDS.has(node.kind()) && nameNode ? nameNode.text() : scope;
|
|
92
|
+
for (const child of node.children())
|
|
93
|
+
walk(child, nextScope);
|
|
94
|
+
};
|
|
95
|
+
walk((0, napi_1.parse)(lang, code).root(), "");
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
/** Defined symbols for a file on disk, or [] if unreadable/unsupported. */
|
|
99
|
+
function definedSymbolsInFile(file) {
|
|
100
|
+
const lang = langForFile(file);
|
|
101
|
+
if (!lang)
|
|
102
|
+
return [];
|
|
103
|
+
try {
|
|
104
|
+
return definedSymbols((0, node_fs_1.readFileSync)(file, "utf-8"), lang);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// A co-located declaration file that may declare symbols the source defines
|
|
111
|
+
// dynamically (Sorbet `.rbi`, TypeScript `.d.ts`) — checked as a fallback so a
|
|
112
|
+
// metaprogrammed `define_method` / ambient declaration still resolves.
|
|
113
|
+
const DECL_SIBLING = {
|
|
114
|
+
".ts": ".d.ts",
|
|
115
|
+
".tsx": ".d.ts",
|
|
116
|
+
".js": ".d.ts",
|
|
117
|
+
".jsx": ".d.ts",
|
|
118
|
+
".mjs": ".d.ts",
|
|
119
|
+
".rb": ".rbi",
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Whether `file` defines a top-level (or scoped) symbol named `name`. This is
|
|
123
|
+
* the whole check for a file-qualified reference (`path#symbol`): we parse the
|
|
124
|
+
* one named file — no project-wide index, no resolution across files. As a
|
|
125
|
+
* fallback we also consult a co-located declaration file (`.rbi` / `.d.ts`), so
|
|
126
|
+
* typed dynamic symbols resolve without running Sorbet / the TS compiler.
|
|
127
|
+
*/
|
|
128
|
+
function fileDefinesSymbol(file, name) {
|
|
129
|
+
if (definedSymbolsInFile(file).some((d) => d.name === name))
|
|
130
|
+
return true;
|
|
131
|
+
const ext = (0, node_path_1.extname)(file);
|
|
132
|
+
const decl = DECL_SIBLING[ext];
|
|
133
|
+
if (decl && !file.endsWith(decl)) {
|
|
134
|
+
const sibling = file.slice(0, -ext.length) + decl;
|
|
135
|
+
if ((0, node_fs_1.existsSync)(sibling) &&
|
|
136
|
+
definedSymbolsInFile(sibling).some((d) => d.name === name)) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ClaudeSpec } from "./spec.js";
|
|
2
|
+
export declare function makeTmpDir(suffix?: string): string;
|
|
3
|
+
export declare function cleanupTmpDir(dir: string): void;
|
|
4
|
+
export declare function makeSpec(overrides?: Partial<ClaudeSpec>): ClaudeSpec;
|
|
5
|
+
declare function git(cwd: string, cmd: string): string;
|
|
6
|
+
export declare function initGitRepo(dir: string): void;
|
|
7
|
+
export { git };
|
|
8
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeTmpDir = makeTmpDir;
|
|
4
|
+
exports.cleanupTmpDir = cleanupTmpDir;
|
|
5
|
+
exports.makeSpec = makeSpec;
|
|
6
|
+
exports.initGitRepo = initGitRepo;
|
|
7
|
+
exports.git = git;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
function makeTmpDir(suffix = "test") {
|
|
13
|
+
return (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), `vigiles-${suffix}-`));
|
|
14
|
+
}
|
|
15
|
+
function cleanupTmpDir(dir) {
|
|
16
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
function makeSpec(overrides) {
|
|
19
|
+
return {
|
|
20
|
+
_specType: "claude",
|
|
21
|
+
rules: {},
|
|
22
|
+
...overrides,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function git(cwd, cmd) {
|
|
26
|
+
return (0, node_child_process_1.execSync)(`git ${cmd}`, {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
function initGitRepo(dir) {
|
|
33
|
+
git(dir, "init");
|
|
34
|
+
git(dir, "config user.email test@test.com");
|
|
35
|
+
git(dir, "config user.name Test");
|
|
36
|
+
git(dir, "config commit.gpgsign false");
|
|
37
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "README.md"), "# test");
|
|
38
|
+
git(dir, "add .");
|
|
39
|
+
git(dir, 'commit -m "init"');
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=test-utils.js.map
|