loopgraph 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.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/adapters/agenthub/extract.d.ts +63 -0
- package/dist/adapters/agenthub/extract.js +144 -0
- package/dist/adapters/agenthub/facts.d.ts +27 -0
- package/dist/adapters/agenthub/facts.js +89 -0
- package/dist/adapters/agenthub/index.d.ts +13 -0
- package/dist/adapters/agenthub/index.js +18 -0
- package/dist/adapters/registry.d.ts +4 -0
- package/dist/adapters/registry.js +17 -0
- package/dist/adapters/types.d.ts +40 -0
- package/dist/adapters/types.js +2 -0
- package/dist/cache/content-cache.d.ts +46 -0
- package/dist/cache/content-cache.js +123 -0
- package/dist/cli/args.d.ts +7 -0
- package/dist/cli/args.js +26 -0
- package/dist/cli/assets/agent-kit.d.ts +15 -0
- package/dist/cli/assets/agent-kit.js +167 -0
- package/dist/cli/commands/check.d.ts +38 -0
- package/dist/cli/commands/check.js +70 -0
- package/dist/cli/commands/import.d.ts +6 -0
- package/dist/cli/commands/import.js +34 -0
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.js +70 -0
- package/dist/cli/commands/inspect.d.ts +23 -0
- package/dist/cli/commands/inspect.js +35 -0
- package/dist/cli/commands/snapshot.d.ts +121 -0
- package/dist/cli/commands/snapshot.js +0 -0
- package/dist/cli/commands/view.d.ts +34 -0
- package/dist/cli/commands/view.js +45 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +329 -0
- package/dist/loader/find-yaml-files.d.ts +26 -0
- package/dist/loader/find-yaml-files.js +51 -0
- package/dist/loader/index.d.ts +4 -0
- package/dist/loader/index.js +4 -0
- package/dist/loader/load-model.d.ts +32 -0
- package/dist/loader/load-model.js +54 -0
- package/dist/loader/model-graph.d.ts +43 -0
- package/dist/loader/model-graph.js +78 -0
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.js +53 -0
- package/dist/query/diff.d.ts +47 -0
- package/dist/query/diff.js +130 -0
- package/dist/query/effective-constraints.d.ts +48 -0
- package/dist/query/effective-constraints.js +81 -0
- package/dist/query/index.d.ts +7 -0
- package/dist/query/index.js +7 -0
- package/dist/query/queries.d.ts +111 -0
- package/dist/query/queries.js +172 -0
- package/dist/query/run-query.d.ts +16 -0
- package/dist/query/run-query.js +181 -0
- package/dist/query/side-channel.d.ts +25 -0
- package/dist/query/side-channel.js +46 -0
- package/dist/query/slice.d.ts +57 -0
- package/dist/query/slice.js +124 -0
- package/dist/query/summary.d.ts +9 -0
- package/dist/query/summary.js +180 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/model.d.ts +661 -0
- package/dist/schema/model.js +183 -0
- package/dist/schema/status.d.ts +18 -0
- package/dist/schema/status.js +28 -0
- package/dist/staleness.d.ts +57 -0
- package/dist/staleness.js +148 -0
- package/dist/validate/anchor.d.ts +4 -0
- package/dist/validate/anchor.js +32 -0
- package/dist/validate/baseline.d.ts +11 -0
- package/dist/validate/baseline.js +18 -0
- package/dist/validate/checks.d.ts +59 -0
- package/dist/validate/checks.js +303 -0
- package/dist/validate/index.d.ts +6 -0
- package/dist/validate/index.js +6 -0
- package/dist/validate/inv1/check.d.ts +36 -0
- package/dist/validate/inv1/check.js +96 -0
- package/dist/validate/inv1/config.d.ts +75 -0
- package/dist/validate/inv1/config.js +63 -0
- package/dist/validate/inv1/scan.d.ts +49 -0
- package/dist/validate/inv1/scan.js +172 -0
- package/dist/validate/t0.d.ts +27 -0
- package/dist/validate/t0.js +31 -0
- package/dist/validate/types.d.ts +16 -0
- package/dist/validate/types.js +4 -0
- package/dist/validate/unregistered.d.ts +70 -0
- package/dist/validate/unregistered.js +111 -0
- package/dist/views/flow-mermaid.d.ts +30 -0
- package/dist/views/flow-mermaid.js +89 -0
- package/dist/views/index.d.ts +3 -0
- package/dist/views/index.js +3 -0
- package/dist/views/validate-mermaid.d.ts +39 -0
- package/dist/views/validate-mermaid.js +110 -0
- package/package.json +46 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, readdir, rename, stat, unlink, utimes, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Machine-level content-addressed cache (Proposal 006 B3 / 001 §5) for the pure
|
|
7
|
+
* per-file extractor/scanner results (A6 INV-1 scan, B1 fact extraction).
|
|
8
|
+
*
|
|
9
|
+
* Key = sha256 over caller-supplied parts — for the fact extractor,
|
|
10
|
+
* (adapter_version, repo-relative path, content_hash). Every part is
|
|
11
|
+
* worktree-INDEPENDENT: the content hash is by definition, and the
|
|
12
|
+
* repo-relative path is the same in every worktree of the repo. So identical
|
|
13
|
+
* bytes at the same in-repo path share ONE entry across the user's many
|
|
14
|
+
* worktrees (the cross-worktree sharing 001 §5 calls for), while the path stays
|
|
15
|
+
* in the key so two files with the same bytes at DIFFERENT paths don't collide
|
|
16
|
+
* (the cached value embeds its path). Content-addressing also makes writes
|
|
17
|
+
* idempotent: a given key's value is deterministic, so a concurrent write of
|
|
18
|
+
* the same key is harmless (both write identical bytes; temp+rename just makes
|
|
19
|
+
* each atomic).
|
|
20
|
+
*
|
|
21
|
+
* Correctness has ZERO cache dependency (001 §6 red line): the value is exactly
|
|
22
|
+
* `compute()` — a cold run (empty cache) and a warm run are byte-identical, and
|
|
23
|
+
* a missing/corrupt/disabled cache just recomputes. `cacheDir: undefined`
|
|
24
|
+
* disables it entirely (the path CI can take to prove independence).
|
|
25
|
+
*
|
|
26
|
+
* Not modeled (documented deferral): the git-index fast-path for reading a
|
|
27
|
+
* clean tracked file's blob OID without re-hashing. Hashing the content
|
|
28
|
+
* directly is always correct and worktree-identical; the index lookup is only a
|
|
29
|
+
* micro-optimization, so B3 ships the always-correct content-hash path.
|
|
30
|
+
*/
|
|
31
|
+
export function defaultCacheDir() {
|
|
32
|
+
return join(homedir(), ".cache", "loopgraph");
|
|
33
|
+
}
|
|
34
|
+
/** sha256 of a file's content — the worktree-independent part of the cache key. */
|
|
35
|
+
export function contentHash(content) {
|
|
36
|
+
return createHash("sha256").update(content).digest("hex");
|
|
37
|
+
}
|
|
38
|
+
/** The cache key: a hash over the ordered key parts (adapter version, content hash, config hash). */
|
|
39
|
+
export function cacheKey(parts) {
|
|
40
|
+
const h = createHash("sha256");
|
|
41
|
+
for (const p of parts) {
|
|
42
|
+
h.update(p);
|
|
43
|
+
h.update("\0");
|
|
44
|
+
}
|
|
45
|
+
return h.digest("hex");
|
|
46
|
+
}
|
|
47
|
+
function entryPath(cacheDir, key) {
|
|
48
|
+
const shard = join(cacheDir, key.slice(0, 2)); // 2-char shard: bounds any one dir's fan-out
|
|
49
|
+
return { shard, path: join(shard, `${key}.json`) };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read-or-compute. On a hit, returns the parsed cached value; on a miss (or
|
|
53
|
+
* disabled/corrupt cache), computes, atomically writes (temp + rename), and
|
|
54
|
+
* returns. `compute` must be a PURE function of the same inputs the key was
|
|
55
|
+
* derived from — that's what makes the cache sound. `compute` may be sync or
|
|
56
|
+
* async (`await` handles both).
|
|
57
|
+
*/
|
|
58
|
+
export async function withCache(cacheDir, key, compute) {
|
|
59
|
+
if (!cacheDir)
|
|
60
|
+
return compute(); // caching disabled → the zero-dependency path
|
|
61
|
+
const { shard, path } = entryPath(cacheDir, key);
|
|
62
|
+
const cached = await readFile(path, "utf8").catch(() => undefined);
|
|
63
|
+
if (cached !== undefined) {
|
|
64
|
+
try {
|
|
65
|
+
const value = JSON.parse(cached);
|
|
66
|
+
// Bump mtime on HIT so it reflects last ACCESS, not last write — a
|
|
67
|
+
// content-addressed entry is never rewritten, so without this the LRU
|
|
68
|
+
// (pruneCache, mtime-sorted) would evict frequently-read hot entries as if
|
|
69
|
+
// they were stale. Best-effort: a failed touch just makes the LRU slightly
|
|
70
|
+
// less accurate, never breaks the read.
|
|
71
|
+
await utimes(path, new Date(), new Date()).catch(() => undefined);
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// corrupt entry (e.g. a torn write from an old non-atomic writer) → recompute + overwrite
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const value = await compute();
|
|
79
|
+
await mkdir(shard, { recursive: true });
|
|
80
|
+
const tmp = `${path}.${randomUUID()}.tmp`;
|
|
81
|
+
await writeFile(tmp, JSON.stringify(value), "utf8");
|
|
82
|
+
// A concurrent writer of the same key wrote identical bytes — rename is still
|
|
83
|
+
// fine; swallow a rare rename error rather than fail the run over the cache.
|
|
84
|
+
await rename(tmp, path).catch(() => undefined);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Bounds the cache to `maxEntries` most-recently-modified entries (the LRU cap
|
|
89
|
+
* from 001 §5). Cheap and best-effort — called once per run, not per file — so
|
|
90
|
+
* an occasional over-count between prunes is fine.
|
|
91
|
+
*/
|
|
92
|
+
export async function pruneCache(cacheDir, maxEntries) {
|
|
93
|
+
const shards = await readdir(cacheDir, { withFileTypes: true }).catch(() => []);
|
|
94
|
+
const entries = [];
|
|
95
|
+
for (const shard of shards) {
|
|
96
|
+
if (!shard.isDirectory())
|
|
97
|
+
continue;
|
|
98
|
+
const shardDir = join(cacheDir, shard.name);
|
|
99
|
+
const files = await readdir(shardDir).catch(() => []);
|
|
100
|
+
for (const f of files) {
|
|
101
|
+
if (!f.endsWith(".json"))
|
|
102
|
+
continue;
|
|
103
|
+
const p = join(shardDir, f);
|
|
104
|
+
const s = await stat(p).catch(() => undefined);
|
|
105
|
+
if (s)
|
|
106
|
+
entries.push({ path: p, mtimeMs: s.mtimeMs });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (entries.length <= maxEntries)
|
|
110
|
+
return 0;
|
|
111
|
+
entries.sort((a, b) => a.mtimeMs - b.mtimeMs); // oldest first
|
|
112
|
+
const toDelete = entries.slice(0, entries.length - maxEntries);
|
|
113
|
+
let deleted = 0;
|
|
114
|
+
for (const e of toDelete) {
|
|
115
|
+
await unlink(e.path)
|
|
116
|
+
.then(() => {
|
|
117
|
+
deleted++;
|
|
118
|
+
})
|
|
119
|
+
.catch(() => undefined);
|
|
120
|
+
}
|
|
121
|
+
return deleted;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=content-cache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ParsedArgs {
|
|
2
|
+
positionals: string[];
|
|
3
|
+
flags: Record<string, string | boolean>;
|
|
4
|
+
}
|
|
5
|
+
/** Minimal `--flag value` / `--flag` (boolean) parser — no dependency for 3 subcommands. */
|
|
6
|
+
export declare function parseFlags(args: string[]): ParsedArgs;
|
|
7
|
+
//# sourceMappingURL=args.d.ts.map
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Minimal `--flag value` / `--flag` (boolean) parser — no dependency for 3 subcommands. */
|
|
2
|
+
export function parseFlags(args) {
|
|
3
|
+
const positionals = [];
|
|
4
|
+
const flags = {};
|
|
5
|
+
for (let i = 0; i < args.length; i++) {
|
|
6
|
+
const arg = args[i];
|
|
7
|
+
if (arg === undefined)
|
|
8
|
+
continue;
|
|
9
|
+
if (arg.startsWith("--")) {
|
|
10
|
+
const key = arg.slice(2);
|
|
11
|
+
const next = args[i + 1];
|
|
12
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
13
|
+
flags[key] = next;
|
|
14
|
+
i++;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
flags[key] = true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
positionals.push(arg);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { positionals, flags };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent kit `loopgraph init` drops into a target repo (Proposal 009):
|
|
3
|
+
* instruction files a coding agent in that repo reads and executes. Shipped as
|
|
4
|
+
* embedded constants because the npm package publishes `dist/` only.
|
|
5
|
+
*
|
|
6
|
+
* PRIVACY: these constants compile into the PUBLIC npm package. They must stay
|
|
7
|
+
* GENERIC — no target-repo internals (table names, file paths, loop ids). The
|
|
8
|
+
* calibrated versions with real teaching examples live in docs/prompts/
|
|
9
|
+
* (repo-private, excluded from the package). A structural test keeps the pass
|
|
10
|
+
* skeleton in sync and asserts no target internals leak here.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LOOP_DISCOVERY_PROMPT = "# Loop Discovery \u63D0\u793A\u8BCD(\u56DB\u904D\u626B\u63CF\u6CD5)\n\n\u7ED9 agent \u7528\u7684\u63A7\u5236\u5FAA\u73AF\u53D1\u73B0\u65B9\u6CD5:**\u5047\u8BBE\u90FD\u662F loop \u2192 \u8BC1\u4F2A \u2192 \u7EC4\u5408 \u2192 trace \u5347\u7EF4**\u3002\n\n## \u524D\u7F6E\u94C1\u5F8B(\u6BCF\u4E00\u904D\u90FD\u9002\u7528)\n\n1. **\u6BCF\u6761\u65AD\u8A00\u5FC5\u987B\u5E26 `\u6587\u4EF6\u8DEF\u5F84#\u7B26\u53F7` \u951A\u70B9,\u4E14 grep \u5BF9\u76EE\u6807\u4ED3 HEAD \u6838\u5B9E**\u3002\u6838\u5B9E\u4E0D\u5230\u5C31\u6807 unverified,\u4E0D\u7F16\u9020\u3002\n2. \u4EA7\u51FA\u5199\u6210 loopgraph model YAML \u8349\u6848(loop / junction / flow / debt),\u4EA4 `loopgraph check --strict-anchors` \u6838\u951A\u70B9\u3001`loopgraph reconcile` \u53CD\u5411\u5BF9\u8D26\u2014\u2014**LLM \u8D1F\u8D23\u53D1\u73B0,\u5F15\u64CE\u8D1F\u8D23\u6838\u5BF9**\u3002\n3. \u8FD0\u884C\u6570\u636E\u53EA\u4F5C\u5B9E\u73B0\u72B6\u6001\u8BC1\u636E,\u4E0D\u53CD\u5411\u6539\u6A21\u578B\u8BED\u4E49\u3002\n\n## Pass 1 \u2014 \u7A77\u4E3E\u5019\u9009(\u5047\u8BBE\u90FD\u662F loop)\n\n**\u5FC3\u6CD5:\u5B81\u6EE5\u52FF\u7F3A\u3002\u4E3E\u8BC1\u8D23\u4EFB\u5728\u8BC1\u4F2A\u7AEF,\u8FD9\u4E00\u904D\u53EA\u7BA1\u53EC\u56DE\u3002**\n\n\u6309\u4FE1\u53F7\u65CF\u5730\u6BEF\u5F0F\u626B,\u6BCF\u4E2A\u547D\u4E2D\u5148\u5047\u8BBE\u662F\u4E00\u4E2A loop:\n\n| \u4FE1\u53F7\u65CF | \u4F8B |\n|---|---|\n| \u72B6\u6001\u5217/\u679A\u4E3E | `status` / `state` / `phase` \u5217,`VALID_*_TRANSITIONS` \u8F6C\u79FB\u8868 |\n| \u961F\u5217 | `*_QUEUE` const\u3001\u6D88\u8D39\u6CE8\u518C(`.work(` \u7B49)\u3001\u5165\u961F\u8C03\u7528 |\n| \u5B9A\u65F6 | `setInterval` / **\u9012\u5F52 `setTimeout` \u81EA\u6211\u91CD\u6392**(leader \u7EED\u79DF\u3001\u9000\u907F\u91CD\u8BD5\u5E38\u7528\u6B64\u5F62\u6001,\u53EA grep setInterval \u4F1A\u6F0F) / cron / `*_tick` / `*_sweep` |\n| \u4E3B\u52A8\u5FAA\u73AF | `while (true)` / poll / watch / long-poll |\n| \u91CD\u8BD5\u6062\u590D | retry / backoff / `:recover` \u961F\u5217 / reconcile / cleanup |\n| \u534F\u8C03\u539F\u8BED | lease / lock / fencing / singleton key |\n| \u4FDD\u6D3B | heartbeat / keepalive / TTL renewal |\n| \u4E8B\u52A1\u6A21\u5F0F | saga / outbox / \u5E26\u72B6\u6001\u63A8\u8FDB\u7684\u8BA2\u9605 |\n\n\u6BCF\u4E2A\u5019\u9009\u8BB0\u5F55:`\u8349\u540D | \u547D\u4E2D\u4FE1\u53F7 | \u951A\u70B9 | \u7591\u4F3C\u72B6\u6001\u8F7D\u4F53 | \u7591\u4F3C\u63A8\u8FDB\u673A\u5236`\u3002\n\n**\u626B\u63CF\u5B8C\u6574\u6027**:\u6309\u5B50\u76EE\u5F55\u5730\u6BEF\u626B\u5B8C\u540E,**\u5FC5\u987B\u5355\u72EC\u626B\u6240\u6709\u672A\u88AB\u5B50\u76EE\u5F55\u5305\u542B\u7684\u9876\u5C42 .ts \u6587\u4EF6**(`src/*.ts`\u3001\u5305\u6839 `index.ts`\u3001\u975E src \u5165\u53E3\u7B49\u2014\u2014\u65D7\u8230\u72B6\u6001\u673A\u5E38\u5728\u6839\u5C42\u88AB\u6574\u4F53\u6F0F\u6389);\u8F93\u51FA NOTES \u5FC5\u987B**\u5BA3\u544A\u672A\u8986\u76D6\u8FB9\u754C**(\u6D4F\u89C8\u5668\u7AEF\u4EE3\u7801\u3001vendored \u4F9D\u8D56\u3001\u8D8A\u754C\u5305),\u9632\"\u6CA1\u62A5=\u6CA1\u6709\"\u8BEF\u8BFB\u3002\n\n## Pass 2 \u2014 \u8BC1\u4F2A(\u6740\u6B7B\u4F2A loop)\n\n\u5BF9\u6BCF\u4E2A\u5019\u9009\u8DD1\u4E09\u5224\u636E,**\u4EFB\u4F55\u4E00\u6761\u627E\u4E0D\u5230\u5C31\u4E0D\u662F\u72EC\u7ACB loop**:\n\n1. **\u72B6\u6001\u8F7D\u4F53**:\u8FDB\u5EA6\u5B58\u5728\u54EA?(DB \u5217 / \u961F\u5217\u4F4D\u7F6E / \u5185\u5B58+lease)\u2014\u2014\u627E\u4E0D\u5230 \u2192 \u65E0\u72B6\u6001\u5DE5\u5177\u51FD\u6570,\u6740\u3002\n2. **\u72EC\u7ACB\u63A8\u8FDB\u673A\u5236**:\u6CA1\u6709\u8C03\u7528\u8005\u65F6\u8C01\u5728\u63A8\u8FDB\u5B83?(worker / poller / cron / \u8BA2\u9605)\u2014\u2014\u53EA\u88AB\u52A8\u7B49\u8C03\u7528 \u2192 \u6740\u3002\n3. **\u6062\u590D\u8DEF\u5F84**:\u5D29\u6E83/\u9519\u8FC7\u4E4B\u540E\u8C01\u63A5\u7740\u63A8?(**\u660E\u786E\u6CA1\u6709\u4E5F\u884C,\u4F46\u5FC5\u987B\u8BB0\u5F55**)\u3002\n\n**\u6740\u524D\u5FC5\u7B54\u4E09\u95EE(\u4F8B\u5916\u89C4\u5219\u5185\u8054\u5316\u2014\u2014\"\u4F8B\u5916\u8BF4\u660E\"\u5728\u957F\u4EFB\u52A1\u91CC\u4F1A\u88AB\u9057\u5FD8,\u6545\u5347\u683C\u4E3A\u6BCF\u6761 KILLED \u8BB0\u5F55\u7684\u5FC5\u586B\u5B57\u6BB5)**:\n\u6BCF\u6740\u4E00\u4E2A\u5019\u9009,KILLED \u8BB0\u5F55\u5FC5\u987B\u663E\u5F0F\u56DE\u7B54:\n- **Q1 \u63A8\u8FDB\u5668\u662F\u5426\u53EF\u80FD\u5728\u8303\u56F4\u5916?**(\u88AB dispatch/\u5176\u4ED6\u5305\u7684 worker/\u5BA2\u6237\u7AEF\u9A71\u52A8)\u662F \u2192 **stub \u4FDD\u7559**,\u4E0D\u6740(\u6267\u884C\u5E73\u9762 loop \u5929\u7136\"\u63A8\u8FDB\u5668\u5728\u522B\u5904\",\u6781\u6613\u7CFB\u7EDF\u6027\u8FC7\u6740)\u3002\n- **Q2 \u662F\u5426\u8DE8\u7EC4\u4EF6\u95ED\u73AF\u7684\u63A5\u6536\u7AEF?**(\u56DE\u8C03/invalidate/webhook \u6536\u7AEF)\u662F \u2192 \u8BB0 Pass 3 \u95ED\u73AF\u5019\u9009,\u4E0D\u6740\u3002\n- **Q3 \u5F52\u7236\u65F6\u662F\u5426\u6709\u72EC\u7ACB\u72B6\u6001\u5B50\u96C6\u4E0E\u9636\u6BB5\u8FB9\u754C?** \u662F \u2192 \u6807 embedded \u5B50\u673A\u5019\u9009(`parent`+`embedded: true`),\u4E0D\u65E0\u75D5\u6298\u53E0\u3002\n\n**\u5206\u6B67\u7C7B\u6807\u6CE8**:\u8BC1\u4F2A\u6740\u6389\u4EE5\u4E0B\u56DB\u7C7B\u65F6\u5FC5\u987B\u6807 `divergence-class`,\u4EA4\u6A21\u578B\u7EF4\u62A4\u8005\u7EC8\u5BA1(\u65E2\u6709\u5206\u7C7B\u5B66\u53EF\u80FD\u4E0E\u4E09\u5224\u636E\u5206\u6B67,\u4E0D\u7B97 agent \u5B9A\u8BBA):\u5355\u8D9F\u7BA1\u7EBF / \u6709\u754C\u91CD\u8BD5\u94FE / vendor \u5305\u88F9\u673A\u5236 / \u65E0\u63A8\u8FDB resolver\u3002\n\n**KILLED \u8BB0\u5F55\u7684\u56FA\u5B9A\u5B57\u6BB5(\u7ED3\u6784\u5316\u9632\u9057\u6F0F)**:\n```\n- name / anchor / killed_by\n q1_advance_out_of_scope: <no | yes\u2192\u6539 stub \u4FDD\u7559>\n q2_closed_loop_receiver: <no | yes\u2192\u6539 Pass3 \u5019\u9009>\n q3_embedded_state_subset: <no | yes\u2192\u6807 embedded \u5019\u9009>\n divergence_class: <none | \u5355\u8D9F\u7BA1\u7EBF | \u6709\u754C\u91CD\u8BD5\u94FE | vendor\u5305\u88F9 | \u65E0\u63A8\u8FDBresolver>\n disposition: <debt\u6B7B\u72B6\u6001\u673A|debt\u9759\u6001\u6807\u5FD7|\u5BA1\u8BA1\u8D26|\u5F52\u5C5E\u7236loop X|\u6CE8\u518C\u8868|\u65E0\u72B6\u6001\u5DE5\u5177|bootstrap>\n```\n(\u4E09\u95EE\u662F**\u9632\u8FC7\u6740**\u95F8\u95E8,\u53EA\u6302\u5728 KILLED \u4E0A;\u5E78\u5B58\u8005\u65E0\u9700\u8FC7\u3002)\n\n**\u88AB\u6740\u5019\u9009\u4E0D\u662F\u4E22\u5F03,\u6309\u5F52\u5BBF\u5206\u6D41**(\u8FD9\u4E00\u6B65\u4EA7\u51FA\u7684 debt \u548C\u5F52\u5C5E,\u548C loop \u4E00\u6837\u503C\u94B1):\n\n| \u8BC1\u4F2A\u5F62\u6001 | \u5F52\u5BBF |\n|---|---|\n| \u6709 status \u5217\u4F46\u5168\u4ED3\u65E0 transition writer | **debt**(\u6B7B\u72B6\u6001\u673A) |\n| insert-once \u9759\u6001\u6807\u5FD7,\u4ECE\u4E0D\u8FC1\u79FB | **debt** |\n| append-only \u5BA1\u8BA1\u8D26(action \u975E state) | \u4E0D\u5EFA\u6A21 |\n| \u6307\u9488/\u4E0D\u53EF\u53D8\u5FEB\u7167,\u65E0\u751F\u547D\u5468\u671F | \u4E0D\u5EFA\u6A21 |\n| \u7236 loop \u8FD0\u884C\u671F\u5185\u7684\u652F\u6491\u5B9A\u65F6\u5668(\u968F\u67D0\u6B21\u6267\u884C\u542F\u52A8/\u6E05\u7406) | **\u951A\u70B9\u5F52\u5165\u7236 loop** |\n| \u961F\u5217\u540D\u6CE8\u518C\u8868(\u53EA\u5B9A\u4E49\u65E0\u63A8\u8FDB) | **\u6D88\u8D39 loop \u7684 `consumes_queues`** |\n| boot \u4E00\u6B21\u6027\u4EFB\u52A1(seed / migrate) | \u4E0D\u5EFA\u6A21(bootstrap)\u2014\u2014debt \u53EA\u7559\u7ED9\"\u5047\u88C5\u6709\u751F\u547D\u5468\u671F\u7684\u6B7B\u7269\" |\n\n## Pass 3 \u2014 loop \u4E4B\u4E0A\u7684 loop(\u7EC4\u5408)\n\n\u5728\u5E78\u5B58 loop \u4E4B\u95F4\u627E**\u8FB9**,\u8FB9\u805A\u6210\u9AD8\u9636\u7ED3\u6784:\n\n- **\u751F\u4EA7/\u6D88\u8D39\u8FB9**:A enqueue \u7684\u961F\u5217\u88AB B consume(\u961F\u5217\u540D\u5339\u914D)\u2192 \u94FE\u8DEF\u8FB9\u3002\n- **\u5171\u4EAB\u72B6\u6001\u8F7D\u4F53**:\u4E24\u4E2A loop \u5199\u540C\u4E00\u5217 \u2192 **junction \u5019\u9009**(\u5E76\u53D1/\u5355\u5199\u8005\u98CE\u9669,\u6807 risk_class)\u3002\n- **\u770B\u62A4\u8FB9**:W \u76D1\u6D4B X \u7684\u6D3B\u6027\u5E76\u89E6\u53D1\u6062\u590D \u2192 `guarded_by`\u3002\n- **\u5185\u5D4C**:\u5B50\u72B6\u6001\u673A\u5171\u4EAB\u7236\u8FB9\u754C\u4F46\u6709\u81EA\u5DF1\u7684\u72B6\u6001 \u2192 `parent` + `embedded: true`\u3002\n- **\u7AEF\u5230\u7AEF\u6709\u5E8F\u7A7F\u8D8A**(\u4E00\u6B21\u7528\u6237\u53EF\u89C1\u7684\u5B8C\u6574\u65C5\u7A0B\u8DE8\u591A\u4E2A loop)\u2192 **flow**\u3002\n\n\u6BCF\u6761\u8FB9\u7ED9\u8BC1\u636E\u951A\u70B9;\u4EA4\u63A5\u53E3(handoff)\u4F18\u5148\u5EFA junction\u2014\u2014\u8DE8 loop \u4EA4\u63A5\u662F\u4E8B\u6545\u9AD8\u53D1\u533A\u3002\n\n**\u4E24\u6761\u8865\u5145\u89C4\u5219**:\n- **\u8D8A\u754C\u7AEF\u8BB0 stub \u8FB9,\u4E0D\u8981\u4E22**:\u8FB9\u7684\u5BF9\u7AEF\u5728\u626B\u63CF\u8303\u56F4\u5916\u65F6,\u8BB0 `stub` \u60AC\u7A7A\u8FB9\u5E76\u6807\u6CE8\"\u5BF9\u7AEF\u672A\u77E5,\u7591\u5728 X\"\u2014\u2014\u62D2\u7EDD\u7F16\u9020\u662F\u5BF9\u7684,\u4F46\u7EBF\u7D22\u4FDD\u7559\u7ED9\u8DE8\u8303\u56F4\u4E0B\u4E00\u8F6E,\u4E22\u5F03\u7B49\u4E8E\u91CD\u67E5\u3002**stub \u53EA\u8FDB\u62A5\u544A\u7684 NOTES/\u8349\u6848,\u7EDD\u4E0D\u5199\u8FDB model YAML**(\u6A21\u578B\u91CC\u6CA1\u6709 edge \u8282\u70B9;\u8FB9\u53EA\u5728\u6838\u5B9E\u53CC\u7AEF\u540E\u7269\u5316\u4E3A junction/flow \u5F15\u7528)\u3002\n- **\u4EA4\u63A5\u53E3\u9644\u8FD1\u6316\u4E8B\u6545\u6CE8\u91CA**:\u5BF9\u6BCF\u4E2A handoff \u5019\u9009,grep \u9644\u8FD1\u6CE8\u91CA\u7684 incident / race / postmortem / \u65E5\u671F / bug \u5F15\u7528\u2014\u2014**\u6709\u771F\u5B9E\u4E8B\u6545\u80CC\u4E66\u7684 junction \u4F18\u5148\u7EA7\u6700\u9AD8**(\u5DF2\u4ED8\u8FC7\u5B66\u8D39\u7684\u98CE\u9669\u70B9)\u3002\n\n## Pass 4 \u2014 trace \u5347\u7EF4(\u6709\u8FD0\u884C\u65F6\u6570\u636E\u65F6)\n\n- \u62C9**\u670D\u52A1\u62D3\u6251 + span \u94FE**:\u8DE8\u670D\u52A1\u95ED\u73AF \u2192 \u9A8C\u8BC1/\u53D1\u73B0\u9759\u6001\u65E0\u6CD5\u62FC\u63A5\u7684\u94FE\u8DEF\u3002\n- **\u53EA\u5728\u8FD0\u884C\u65F6\u51FA\u73B0\u7684 loop**:\u5BA2\u6237\u7AEF\u8F6E\u8BE2\u3001\u7F51\u5173\u91CD\u8BD5\u3001\u5916\u90E8\u56DE\u8C03\u95ED\u73AF\u3002\n- **\u9891\u7387/\u5EF6\u8FDF/\u91CD\u8BD5\u7387\u805A\u5408** \u2192 junction \u98CE\u9669\u5206\u7EA7\u7684\u91CF\u5316\u8BC1\u636E\u3002\n- **\u53CC\u5411\u5BF9\u8D26**:trace \u91CC\u6D3B\u8DC3\u4F46\u6A21\u578B\u6CA1\u6709 \u2192 \u65B0\u53D1\u73B0\u5019\u9009(\u56DE Pass 2 \u8BC1\u4F2A);\u6A21\u578B\u6709\u4F46 trace \u957F\u671F\u65E0 \u2192 dormant \u5019\u9009(\u4E0D\u662F\u5220\u9664,\u6807 dormant)\u3002\n- **\u964D\u7EA7\u89C4\u5219(\u51B7\u542F\u52A8/\u4F4E\u91C7\u6837\u515C\u5E95)**:trace \u7F3A\u5931\u3001\u91C7\u6837\u7387\u4F4E\u6216\u89C2\u6D4B\u7A97\u53E3\u4E0D\u8DB3\u65F6,**\u6302\u8D77 dormant \u5224\u5B9A**(\u672A\u88AB\u91C7\u6837 \u2260 \u4E0D\u6D3B\u8DC3),\u53EA\u505A\u6B63\u5411\u53D1\u73B0,\u6A21\u578B\u5224\u5B9A\u9000\u56DE Pass 1\u20133 \u7EAF\u9759\u6001\u3002dormant \u5224\u5B9A\u5FC5\u987B\u6CE8\u660E\u89C2\u6D4B\u7A97\u53E3\u4E0E\u91C7\u6837\u7387\u3002\n\n## \u8F93\u51FA\u4E0E\u95ED\u73AF\n\n```\nLLM \u56DB\u904D\u626B\u63CF \u2192 model YAML \u8349\u6848\n \u2192 loopgraph check --strict-anchors # \u951A\u70B9\u771F\u5B9E\u6027(\u673A\u5668)\n \u2192 loopgraph reconcile # \u673A\u68B0\u4FE1\u53F7\u53CD\u5411\u5BF9\u8D26\n \u2192 \u4EBA\u5BA1 \u2192 \u843D\u5E93\n```\n";
|
|
13
|
+
export declare const SETUP_PR_TEMPLATE_PROMPT = "# \u4EFB\u52A1:\u7ED9\u672C\u4ED3\u5E93\u7684 PR \u6A21\u677F\u52A0 Loop \u58F0\u660E\u680F(loopgraph \u540C\u6B65\u901A\u9053 \u2461)\n\n\u4F60\u662F\u672C\u4ED3\u5E93\u7684 coding agent\u3002\u76EE\u6807:\u8BA9\u6BCF\u4E2A PR \u663E\u5F0F\u58F0\u660E\u5B83\u662F\u5426\u5F15\u5165/\u4FEE\u6539\u63A7\u5236\u5FAA\u73AF(loop),\u4E0E loopgraph \u7684\u786E\u5B9A\u6027\u5BF9\u8D26(\u901A\u9053 \u2460)\u4E92\u8BC1\u3002\n\n## \u6B65\u9AA4\n\n1. **\u627E\u5230\u73B0\u6709 PR/MR \u6A21\u677F**:GitHub \u4E3A `.github/pull_request_template.md` \u6216 `.github/PULL_REQUEST_TEMPLATE/*.md`;GitLab \u4E3A `.gitlab/merge_request_templates/*.md`\u3002\u6CA1\u6709\u5C31\u65B0\u5EFA\u5E73\u53F0\u5BF9\u5E94\u7684\u9ED8\u8BA4\u6A21\u677F\u3002\n2. **\u5E42\u7B49**:\u82E5\u6A21\u677F\u5DF2\u542B \"Loop \u58F0\u660E\" \u8282,\u4E0D\u8981\u91CD\u590D\u6DFB\u52A0\u3002\n3. **\u8FFD\u52A0\u4EE5\u4E0B\u8282**(\u4E0D\u6539\u52A8\u6A21\u677F\u5176\u4F59\u5185\u5BB9\u3001\u4E0D\u91CD\u6392\u683C\u5F0F):\n\n```markdown\n## Loop \u58F0\u660E\n\n<!-- \u672C PR \u662F\u5426\u5F15\u5165\u6216\u4FEE\u6539\u540E\u53F0\u63A7\u5236\u5FAA\u73AF(\u961F\u5217\u6D88\u8D39/\u5B9A\u65F6\u5668/\u72B6\u6001\u673A/\u8F6E\u8BE2/\u91CD\u8BD5\u94FE)? -->\n- [ ] none \u2014 \u4E0D\u6D89\u53CA\u4EFB\u4F55 loop\n- [ ] \u5F15\u5165/\u4FEE\u6539: <loop id \u5217\u8868,\u5982 L2 / \u65B0 loop \u63CF\u8FF0>\n- [ ] uncertain \u2014 \u4E0D\u786E\u5B9A(\u5C06\u89E6\u53D1 loop \u53D1\u73B0 agent \u5206\u6790\u672C PR)\n```\n\n## \u89C4\u5219\n\n- \u58F0\u660E\u4E3A `uncertain` \u6216\"\u5F15\u5165/\u4FEE\u6539\"\u7684 PR,\u4F1A\u89E6\u53D1 advisory \u5206\u6790(\u901A\u9053 \u2462);\u58F0\u660E none \u4F46\u786E\u5B9A\u6027\u5BF9\u8D26\u6293\u5230\u65B0\u961F\u5217/\u5B9A\u65F6\u5668\u4FE1\u53F7 \u2192 \u77DB\u76FE,\u8981\u6C42\u6539\u58F0\u660E\u3002\n- \u63D0\u4EA4\u8FD9\u4E2A\u6539\u52A8\u65F6\u8BF4\u660E:\u8FD9\u662F\u58F0\u660E\u680F,\u4E0D\u662F\u95E8\u7981\u2014\u2014\u95E8\u7981\u53EA\u6709\u786E\u5B9A\u6027\u7684 `loopgraph check`\u3002\n";
|
|
14
|
+
export declare const SETUP_GITHUB_ACTIONS_PROMPT = "# \u4EFB\u52A1:\u4E3A\u672C\u4ED3\u5E93\u7F16\u5199 loopgraph \u7684\u4E24\u4E2A GitHub Actions(\u901A\u9053 \u2462 advisory + \u2463 nightly)\n\n\u4F60\u662F\u672C\u4ED3\u5E93\u7684 coding agent\u3002**\u5148\u8BFB `.github/workflows/` \u4E0B\u7684\u73B0\u6709 workflow**,\u6CBF\u7528\u672C\u4ED3\u5E93\u7684 runner\u3001Node \u7248\u672C\u3001\u7F13\u5B58\u3001secrets \u547D\u540D\u7B49\u7EA6\u5B9A\u2014\u2014\u4E0D\u8981\u51ED\u7A7A\u53D1\u660E\u98CE\u683C\u3002\n\n## \u7EA2\u7EBF(\u4E24\u4E2A workflow \u5171\u540C\u9075\u5B88)\n\n- **\u6C38\u4E0D\u963B\u585E PR**:\u4E24\u4E2A job \u90FD\u4E0D\u5F97\u6210\u4E3A required check;advisory job \u5931\u8D25\u4E0D\u5141\u8BB8\u6302 PR(\u7ED3\u8BBA\u53EA\u4EE5 comment/artifact \u5F62\u5F0F\u7ED9\u51FA)\u3002\u786E\u5B9A\u6027\u95E8\u7981(`loopgraph check`)\u662F\u53E6\u4E00\u6761\u72EC\u7ACB\u901A\u9053,\u90A3\u91CC\u6C38\u4E0D\u8C03 LLM\u3002\n- agent \u7684 API key \u4ECE\u4ED3\u5E93 secrets \u8BFB;\u82E5\u672C\u4ED3\u5E93\u6CA1\u6709\u53EF\u7528\u7684 agent \u8FD0\u884C\u5668,\u751F\u6210\u5E26 TODO \u6CE8\u91CA\u7684 workflow \u9AA8\u67B6\u5E76\u5728 PR \u63CF\u8FF0\u4E2D\u8BF4\u660E\u7F3A\u4EC0\u4E48\u3002\n\n## Workflow \u2462 \u2014 PR advisory(loop \u53D1\u73B0\u5EFA\u8BAE,\u975E\u95E8\u7981)\n\n- \u89E6\u53D1:`pull_request`\u3002\n- **\u6761\u4EF6\u6267\u884C**(\u4E0D\u6EE1\u8DB3\u76F4\u63A5 skip,\u7701\u7B97\u529B):PR \u6A21\u677F Loop \u58F0\u660E\u4E3A\"\u5F15\u5165/\u4FEE\u6539\"\u6216 `uncertain`(\u89E3\u6790 PR body);\u6216 diff \u8D85\u8FC7\u7EA6 500 \u884C;\u6216 diff \u547D\u4E2D\u9AD8\u98CE\u9669\u8DEF\u5F84/\u5185\u5BB9(\u72B6\u6001\u5217 schema \u76EE\u5F55\u3001\u961F\u5217 const\u3001`setInterval`\u3001\u6D88\u8D39\u6CE8\u518C\u8C03\u7528\u2014\u2014\u6309\u672C\u4ED3\u5E93\u5B9E\u9645\u76EE\u5F55\u7ED3\u6784\u5B9A glob)\u3002\n- \u6267\u884C:\u8C03\u7528 agent \u8FD0\u884C\u5668,\u8F93\u5165 `.loopgraph/agent/loop-discovery.md` \u7684 Pass 1\u20133,\u8303\u56F4\u9650\u5B9A\u4E3A\u672C PR \u7684 diff;\u4EA7\u51FA loop/junction YAML \u8349\u6848\u3002\n- \u8F93\u51FA:\u4EE5 **PR comment** \u53D1\u5E03\u53D1\u73B0\u6458\u8981 + \u8349\u6848\u6587\u4EF6\u8DEF\u5F84;\u8349\u6848\u4F5C\u4E3A artifact \u4E0A\u4F20,\u7531\u4F5C\u8005\u51B3\u5B9A\u662F\u5426\u968F PR \u843D\u5165 `model/`\u3002\n\n## Workflow \u2463 \u2014 nightly re-discovery(\u6F02\u79FB\u515C\u5E95)\n\n- \u89E6\u53D1:`schedule`(\u6BCF\u65E5\u4E00\u6B21,\u9009\u4F4E\u5CF0\u65F6\u6BB5)+ `workflow_dispatch`\u3002\n- \u6267\u884C:\u5BF9\u8FC7\u53BB 24h \u53D8\u66F4\u7684\u6E90\u7801\u6587\u4EF6\u8DD1 Pass 1\u20132,\u4E0E `model/` \u73B0\u72B6 diff,\u4EA7\u51FA\"\u7591\u4F3C\u65B0 loop / \u7591\u4F3C\u6F02\u79FB\"\u62A5\u544A\u3002\n- \u8F93\u51FA:\u62A5\u544A\u4F5C\u4E3A artifact;\u53D1\u73B0\u7591\u4F3C\u65B0 loop \u65F6\u5F00\u4E00\u4E2A issue(\u5E42\u7B49:\u5DF2\u6709\u540C\u540D open issue \u5219\u8FFD\u52A0\u8BC4\u8BBA,\u4E0D\u91CD\u590D\u5F00)\u3002\n- \u5B9A\u4F4D:\u8FD9\u662F\u8BA4\u77E5\u76F2\u533A\u7684\u515C\u5E95\u2014\u2014PR \u671F\u58F0\u660E none \u4E14\u672A\u89E6\u53D1 \u2462 \u7684\u8BED\u4E49\u65B0 loop,\u6700\u8FDF\u8FD9\u91CC\u6D6E\u51FA\u3002\n\n## \u9A8C\u6536\n\n- \u4E24\u4E2A workflow \u8BED\u6CD5\u6709\u6548(`actionlint` \u6216 dry-run \u6821\u9A8C)\u3002\n- \u5728 PR \u63CF\u8FF0\u4E2D\u9010\u6761\u8BF4\u660E:\u89E6\u53D1\u6761\u4EF6\u3001\u4E3A\u4EC0\u4E48\u4E0D\u662F required check\u3001secrets \u4F9D\u8D56\u3002\n";
|
|
15
|
+
//# sourceMappingURL=agent-kit.d.ts.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent kit `loopgraph init` drops into a target repo (Proposal 009):
|
|
3
|
+
* instruction files a coding agent in that repo reads and executes. Shipped as
|
|
4
|
+
* embedded constants because the npm package publishes `dist/` only.
|
|
5
|
+
*
|
|
6
|
+
* PRIVACY: these constants compile into the PUBLIC npm package. They must stay
|
|
7
|
+
* GENERIC — no target-repo internals (table names, file paths, loop ids). The
|
|
8
|
+
* calibrated versions with real teaching examples live in docs/prompts/
|
|
9
|
+
* (repo-private, excluded from the package). A structural test keeps the pass
|
|
10
|
+
* skeleton in sync and asserts no target internals leak here.
|
|
11
|
+
*/
|
|
12
|
+
export const LOOP_DISCOVERY_PROMPT = `# Loop Discovery 提示词(四遍扫描法)
|
|
13
|
+
|
|
14
|
+
给 agent 用的控制循环发现方法:**假设都是 loop → 证伪 → 组合 → trace 升维**。
|
|
15
|
+
|
|
16
|
+
## 前置铁律(每一遍都适用)
|
|
17
|
+
|
|
18
|
+
1. **每条断言必须带 \`文件路径#符号\` 锚点,且 grep 对目标仓 HEAD 核实**。核实不到就标 unverified,不编造。
|
|
19
|
+
2. 产出写成 loopgraph model YAML 草案(loop / junction / flow / debt),交 \`loopgraph check --strict-anchors\` 核锚点、\`loopgraph reconcile\` 反向对账——**LLM 负责发现,引擎负责核对**。
|
|
20
|
+
3. 运行数据只作实现状态证据,不反向改模型语义。
|
|
21
|
+
|
|
22
|
+
## Pass 1 — 穷举候选(假设都是 loop)
|
|
23
|
+
|
|
24
|
+
**心法:宁滥勿缺。举证责任在证伪端,这一遍只管召回。**
|
|
25
|
+
|
|
26
|
+
按信号族地毯式扫,每个命中先假设是一个 loop:
|
|
27
|
+
|
|
28
|
+
| 信号族 | 例 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| 状态列/枚举 | \`status\` / \`state\` / \`phase\` 列,\`VALID_*_TRANSITIONS\` 转移表 |
|
|
31
|
+
| 队列 | \`*_QUEUE\` const、消费注册(\`.work(\` 等)、入队调用 |
|
|
32
|
+
| 定时 | \`setInterval\` / **递归 \`setTimeout\` 自我重排**(leader 续租、退避重试常用此形态,只 grep setInterval 会漏) / cron / \`*_tick\` / \`*_sweep\` |
|
|
33
|
+
| 主动循环 | \`while (true)\` / poll / watch / long-poll |
|
|
34
|
+
| 重试恢复 | retry / backoff / \`:recover\` 队列 / reconcile / cleanup |
|
|
35
|
+
| 协调原语 | lease / lock / fencing / singleton key |
|
|
36
|
+
| 保活 | heartbeat / keepalive / TTL renewal |
|
|
37
|
+
| 事务模式 | saga / outbox / 带状态推进的订阅 |
|
|
38
|
+
|
|
39
|
+
每个候选记录:\`草名 | 命中信号 | 锚点 | 疑似状态载体 | 疑似推进机制\`。
|
|
40
|
+
|
|
41
|
+
**扫描完整性**:按子目录地毯扫完后,**必须单独扫所有未被子目录包含的顶层 .ts 文件**(\`src/*.ts\`、包根 \`index.ts\`、非 src 入口等——旗舰状态机常在根层被整体漏掉);输出 NOTES 必须**宣告未覆盖边界**(浏览器端代码、vendored 依赖、越界包),防"没报=没有"误读。
|
|
42
|
+
|
|
43
|
+
## Pass 2 — 证伪(杀死伪 loop)
|
|
44
|
+
|
|
45
|
+
对每个候选跑三判据,**任何一条找不到就不是独立 loop**:
|
|
46
|
+
|
|
47
|
+
1. **状态载体**:进度存在哪?(DB 列 / 队列位置 / 内存+lease)——找不到 → 无状态工具函数,杀。
|
|
48
|
+
2. **独立推进机制**:没有调用者时谁在推进它?(worker / poller / cron / 订阅)——只被动等调用 → 杀。
|
|
49
|
+
3. **恢复路径**:崩溃/错过之后谁接着推?(**明确没有也行,但必须记录**)。
|
|
50
|
+
|
|
51
|
+
**杀前必答三问(例外规则内联化——"例外说明"在长任务里会被遗忘,故升格为每条 KILLED 记录的必填字段)**:
|
|
52
|
+
每杀一个候选,KILLED 记录必须显式回答:
|
|
53
|
+
- **Q1 推进器是否可能在范围外?**(被 dispatch/其他包的 worker/客户端驱动)是 → **stub 保留**,不杀(执行平面 loop 天然"推进器在别处",极易系统性过杀)。
|
|
54
|
+
- **Q2 是否跨组件闭环的接收端?**(回调/invalidate/webhook 收端)是 → 记 Pass 3 闭环候选,不杀。
|
|
55
|
+
- **Q3 归父时是否有独立状态子集与阶段边界?** 是 → 标 embedded 子机候选(\`parent\`+\`embedded: true\`),不无痕折叠。
|
|
56
|
+
|
|
57
|
+
**分歧类标注**:证伪杀掉以下四类时必须标 \`divergence-class\`,交模型维护者终审(既有分类学可能与三判据分歧,不算 agent 定论):单趟管线 / 有界重试链 / vendor 包裹机制 / 无推进 resolver。
|
|
58
|
+
|
|
59
|
+
**KILLED 记录的固定字段(结构化防遗漏)**:
|
|
60
|
+
\`\`\`
|
|
61
|
+
- name / anchor / killed_by
|
|
62
|
+
q1_advance_out_of_scope: <no | yes→改 stub 保留>
|
|
63
|
+
q2_closed_loop_receiver: <no | yes→改 Pass3 候选>
|
|
64
|
+
q3_embedded_state_subset: <no | yes→标 embedded 候选>
|
|
65
|
+
divergence_class: <none | 单趟管线 | 有界重试链 | vendor包裹 | 无推进resolver>
|
|
66
|
+
disposition: <debt死状态机|debt静态标志|审计账|归属父loop X|注册表|无状态工具|bootstrap>
|
|
67
|
+
\`\`\`
|
|
68
|
+
(三问是**防过杀**闸门,只挂在 KILLED 上;幸存者无需过。)
|
|
69
|
+
|
|
70
|
+
**被杀候选不是丢弃,按归宿分流**(这一步产出的 debt 和归属,和 loop 一样值钱):
|
|
71
|
+
|
|
72
|
+
| 证伪形态 | 归宿 |
|
|
73
|
+
|---|---|
|
|
74
|
+
| 有 status 列但全仓无 transition writer | **debt**(死状态机) |
|
|
75
|
+
| insert-once 静态标志,从不迁移 | **debt** |
|
|
76
|
+
| append-only 审计账(action 非 state) | 不建模 |
|
|
77
|
+
| 指针/不可变快照,无生命周期 | 不建模 |
|
|
78
|
+
| 父 loop 运行期内的支撑定时器(随某次执行启动/清理) | **锚点归入父 loop** |
|
|
79
|
+
| 队列名注册表(只定义无推进) | **消费 loop 的 \`consumes_queues\`** |
|
|
80
|
+
| boot 一次性任务(seed / migrate) | 不建模(bootstrap)——debt 只留给"假装有生命周期的死物" |
|
|
81
|
+
|
|
82
|
+
## Pass 3 — loop 之上的 loop(组合)
|
|
83
|
+
|
|
84
|
+
在幸存 loop 之间找**边**,边聚成高阶结构:
|
|
85
|
+
|
|
86
|
+
- **生产/消费边**:A enqueue 的队列被 B consume(队列名匹配)→ 链路边。
|
|
87
|
+
- **共享状态载体**:两个 loop 写同一列 → **junction 候选**(并发/单写者风险,标 risk_class)。
|
|
88
|
+
- **看护边**:W 监测 X 的活性并触发恢复 → \`guarded_by\`。
|
|
89
|
+
- **内嵌**:子状态机共享父边界但有自己的状态 → \`parent\` + \`embedded: true\`。
|
|
90
|
+
- **端到端有序穿越**(一次用户可见的完整旅程跨多个 loop)→ **flow**。
|
|
91
|
+
|
|
92
|
+
每条边给证据锚点;交接口(handoff)优先建 junction——跨 loop 交接是事故高发区。
|
|
93
|
+
|
|
94
|
+
**两条补充规则**:
|
|
95
|
+
- **越界端记 stub 边,不要丢**:边的对端在扫描范围外时,记 \`stub\` 悬空边并标注"对端未知,疑在 X"——拒绝编造是对的,但线索保留给跨范围下一轮,丢弃等于重查。**stub 只进报告的 NOTES/草案,绝不写进 model YAML**(模型里没有 edge 节点;边只在核实双端后物化为 junction/flow 引用)。
|
|
96
|
+
- **交接口附近挖事故注释**:对每个 handoff 候选,grep 附近注释的 incident / race / postmortem / 日期 / bug 引用——**有真实事故背书的 junction 优先级最高**(已付过学费的风险点)。
|
|
97
|
+
|
|
98
|
+
## Pass 4 — trace 升维(有运行时数据时)
|
|
99
|
+
|
|
100
|
+
- 拉**服务拓扑 + span 链**:跨服务闭环 → 验证/发现静态无法拼接的链路。
|
|
101
|
+
- **只在运行时出现的 loop**:客户端轮询、网关重试、外部回调闭环。
|
|
102
|
+
- **频率/延迟/重试率聚合** → junction 风险分级的量化证据。
|
|
103
|
+
- **双向对账**:trace 里活跃但模型没有 → 新发现候选(回 Pass 2 证伪);模型有但 trace 长期无 → dormant 候选(不是删除,标 dormant)。
|
|
104
|
+
- **降级规则(冷启动/低采样兜底)**:trace 缺失、采样率低或观测窗口不足时,**挂起 dormant 判定**(未被采样 ≠ 不活跃),只做正向发现,模型判定退回 Pass 1–3 纯静态。dormant 判定必须注明观测窗口与采样率。
|
|
105
|
+
|
|
106
|
+
## 输出与闭环
|
|
107
|
+
|
|
108
|
+
\`\`\`
|
|
109
|
+
LLM 四遍扫描 → model YAML 草案
|
|
110
|
+
→ loopgraph check --strict-anchors # 锚点真实性(机器)
|
|
111
|
+
→ loopgraph reconcile # 机械信号反向对账
|
|
112
|
+
→ 人审 → 落库
|
|
113
|
+
\`\`\`
|
|
114
|
+
`;
|
|
115
|
+
export const SETUP_PR_TEMPLATE_PROMPT = `# 任务:给本仓库的 PR 模板加 Loop 声明栏(loopgraph 同步通道 ②)
|
|
116
|
+
|
|
117
|
+
你是本仓库的 coding agent。目标:让每个 PR 显式声明它是否引入/修改控制循环(loop),与 loopgraph 的确定性对账(通道 ①)互证。
|
|
118
|
+
|
|
119
|
+
## 步骤
|
|
120
|
+
|
|
121
|
+
1. **找到现有 PR/MR 模板**:GitHub 为 \`.github/pull_request_template.md\` 或 \`.github/PULL_REQUEST_TEMPLATE/*.md\`;GitLab 为 \`.gitlab/merge_request_templates/*.md\`。没有就新建平台对应的默认模板。
|
|
122
|
+
2. **幂等**:若模板已含 "Loop 声明" 节,不要重复添加。
|
|
123
|
+
3. **追加以下节**(不改动模板其余内容、不重排格式):
|
|
124
|
+
|
|
125
|
+
\`\`\`markdown
|
|
126
|
+
## Loop 声明
|
|
127
|
+
|
|
128
|
+
<!-- 本 PR 是否引入或修改后台控制循环(队列消费/定时器/状态机/轮询/重试链)? -->
|
|
129
|
+
- [ ] none — 不涉及任何 loop
|
|
130
|
+
- [ ] 引入/修改: <loop id 列表,如 L2 / 新 loop 描述>
|
|
131
|
+
- [ ] uncertain — 不确定(将触发 loop 发现 agent 分析本 PR)
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
## 规则
|
|
135
|
+
|
|
136
|
+
- 声明为 \`uncertain\` 或"引入/修改"的 PR,会触发 advisory 分析(通道 ③);声明 none 但确定性对账抓到新队列/定时器信号 → 矛盾,要求改声明。
|
|
137
|
+
- 提交这个改动时说明:这是声明栏,不是门禁——门禁只有确定性的 \`loopgraph check\`。
|
|
138
|
+
`;
|
|
139
|
+
export const SETUP_GITHUB_ACTIONS_PROMPT = `# 任务:为本仓库编写 loopgraph 的两个 GitHub Actions(通道 ③ advisory + ④ nightly)
|
|
140
|
+
|
|
141
|
+
你是本仓库的 coding agent。**先读 \`.github/workflows/\` 下的现有 workflow**,沿用本仓库的 runner、Node 版本、缓存、secrets 命名等约定——不要凭空发明风格。
|
|
142
|
+
|
|
143
|
+
## 红线(两个 workflow 共同遵守)
|
|
144
|
+
|
|
145
|
+
- **永不阻塞 PR**:两个 job 都不得成为 required check;advisory job 失败不允许挂 PR(结论只以 comment/artifact 形式给出)。确定性门禁(\`loopgraph check\`)是另一条独立通道,那里永不调 LLM。
|
|
146
|
+
- agent 的 API key 从仓库 secrets 读;若本仓库没有可用的 agent 运行器,生成带 TODO 注释的 workflow 骨架并在 PR 描述中说明缺什么。
|
|
147
|
+
|
|
148
|
+
## Workflow ③ — PR advisory(loop 发现建议,非门禁)
|
|
149
|
+
|
|
150
|
+
- 触发:\`pull_request\`。
|
|
151
|
+
- **条件执行**(不满足直接 skip,省算力):PR 模板 Loop 声明为"引入/修改"或 \`uncertain\`(解析 PR body);或 diff 超过约 500 行;或 diff 命中高风险路径/内容(状态列 schema 目录、队列 const、\`setInterval\`、消费注册调用——按本仓库实际目录结构定 glob)。
|
|
152
|
+
- 执行:调用 agent 运行器,输入 \`.loopgraph/agent/loop-discovery.md\` 的 Pass 1–3,范围限定为本 PR 的 diff;产出 loop/junction YAML 草案。
|
|
153
|
+
- 输出:以 **PR comment** 发布发现摘要 + 草案文件路径;草案作为 artifact 上传,由作者决定是否随 PR 落入 \`model/\`。
|
|
154
|
+
|
|
155
|
+
## Workflow ④ — nightly re-discovery(漂移兜底)
|
|
156
|
+
|
|
157
|
+
- 触发:\`schedule\`(每日一次,选低峰时段)+ \`workflow_dispatch\`。
|
|
158
|
+
- 执行:对过去 24h 变更的源码文件跑 Pass 1–2,与 \`model/\` 现状 diff,产出"疑似新 loop / 疑似漂移"报告。
|
|
159
|
+
- 输出:报告作为 artifact;发现疑似新 loop 时开一个 issue(幂等:已有同名 open issue 则追加评论,不重复开)。
|
|
160
|
+
- 定位:这是认知盲区的兜底——PR 期声明 none 且未触发 ③ 的语义新 loop,最迟这里浮出。
|
|
161
|
+
|
|
162
|
+
## 验收
|
|
163
|
+
|
|
164
|
+
- 两个 workflow 语法有效(\`actionlint\` 或 dry-run 校验)。
|
|
165
|
+
- 在 PR 描述中逐条说明:触发条件、为什么不是 required check、secrets 依赖。
|
|
166
|
+
`;
|
|
167
|
+
//# sourceMappingURL=agent-kit.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type AffectedNode } from "../../query/diff.js";
|
|
2
|
+
import { type Inv1CheckResult } from "../../validate/inv1/check.js";
|
|
3
|
+
import type { T0Result, Violation } from "../../validate/types.js";
|
|
4
|
+
export interface CheckOptions {
|
|
5
|
+
repoRoot?: string | undefined;
|
|
6
|
+
strictAnchorExistence?: boolean | undefined;
|
|
7
|
+
/** `--diff <base-ref>`: incremental mode (B2) — scan only the diff closure + map affected nodes. */
|
|
8
|
+
diffBase?: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface DiffInfo {
|
|
11
|
+
baseRef: string;
|
|
12
|
+
/** repo-relative files changed in repoRoot's diff (undefined = couldn't resolve → full scan fallback). */
|
|
13
|
+
changed?: string[];
|
|
14
|
+
/** model nodes whose anchors point at a changed file. */
|
|
15
|
+
affected: AffectedNode[];
|
|
16
|
+
/** true when INV-1 ran incrementally (only changed candidate files). */
|
|
17
|
+
incremental: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface CheckResult {
|
|
20
|
+
t0: T0Result;
|
|
21
|
+
/** Present only when a repoRoot is given AND a valid loopgraph.config.json is found. */
|
|
22
|
+
inv1?: Inv1CheckResult;
|
|
23
|
+
/** Set when a config file exists but is malformed — a loud error, not a silent skip. */
|
|
24
|
+
inv1ConfigError?: string;
|
|
25
|
+
/** Present only with `--diff`. */
|
|
26
|
+
diff?: DiffInfo;
|
|
27
|
+
/** baseline-growth violations from `--diff` (new debt ids vs the base ref). */
|
|
28
|
+
baselineViolations?: Violation[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Runs T0 (model + optional anchor existence), the INV-1 canonical-writer scan
|
|
32
|
+
* (when a repoRoot + config are present), and — with `--diff <base>` — the
|
|
33
|
+
* incremental B2 path: resolve the diff, scan INV-1 over only the changed files,
|
|
34
|
+
* map changed files to affected model nodes, and enforce baseline-only-decreases
|
|
35
|
+
* against the model at the base ref.
|
|
36
|
+
*/
|
|
37
|
+
export declare function runCheck(targetDir: string, options?: CheckOptions): Promise<CheckResult>;
|
|
38
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { join, relative } from "node:path";
|
|
2
|
+
import { loadModel } from "../../loader/load-model.js";
|
|
3
|
+
import { affectedNodes, changedFiles, debtIdsAtRef, gitRootOf, } from "../../query/diff.js";
|
|
4
|
+
import { checkBaselineOnlyDecreases } from "../../validate/baseline.js";
|
|
5
|
+
import { runInv1Check } from "../../validate/inv1/check.js";
|
|
6
|
+
import { loadInv1Config } from "../../validate/inv1/config.js";
|
|
7
|
+
import { runT0 } from "../../validate/t0.js";
|
|
8
|
+
/**
|
|
9
|
+
* Runs T0 (model + optional anchor existence), the INV-1 canonical-writer scan
|
|
10
|
+
* (when a repoRoot + config are present), and — with `--diff <base>` — the
|
|
11
|
+
* incremental B2 path: resolve the diff, scan INV-1 over only the changed files,
|
|
12
|
+
* map changed files to affected model nodes, and enforce baseline-only-decreases
|
|
13
|
+
* against the model at the base ref.
|
|
14
|
+
*/
|
|
15
|
+
export async function runCheck(targetDir, options = {}) {
|
|
16
|
+
const modelDir = join(targetDir, "model");
|
|
17
|
+
const load = await loadModel(modelDir);
|
|
18
|
+
const t0 = await runT0(load, options);
|
|
19
|
+
// --- baseline-only-decreases (B2): model debt at base ref vs now ---
|
|
20
|
+
let baselineViolations;
|
|
21
|
+
if (options.diffBase) {
|
|
22
|
+
const modelGitRoot = await gitRootOf(targetDir);
|
|
23
|
+
if (modelGitRoot) {
|
|
24
|
+
const before = await debtIdsAtRef(modelGitRoot, relative(modelGitRoot, modelDir), options.diffBase);
|
|
25
|
+
if (before) {
|
|
26
|
+
const after = new Set(load.graph.byKind.debt.keys());
|
|
27
|
+
baselineViolations = checkBaselineOnlyDecreases(before, after);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!options.repoRoot)
|
|
32
|
+
return { t0, ...(baselineViolations ? { baselineViolations } : {}) };
|
|
33
|
+
// --- diff resolution over the scanned repo (B2) ---
|
|
34
|
+
let diff;
|
|
35
|
+
let onlyFiles;
|
|
36
|
+
if (options.diffBase) {
|
|
37
|
+
const changed = await changedFiles(options.repoRoot, options.diffBase);
|
|
38
|
+
onlyFiles = changed ? new Set(changed) : undefined;
|
|
39
|
+
diff = {
|
|
40
|
+
baseRef: options.diffBase,
|
|
41
|
+
...(changed ? { changed } : {}),
|
|
42
|
+
affected: changed ? affectedNodes(load.graph, changed) : [],
|
|
43
|
+
incremental: onlyFiles !== undefined,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const configResult = await loadInv1Config(targetDir);
|
|
47
|
+
if (configResult.error) {
|
|
48
|
+
return {
|
|
49
|
+
t0,
|
|
50
|
+
inv1ConfigError: configResult.error,
|
|
51
|
+
...(diff ? { diff } : {}),
|
|
52
|
+
...(baselineViolations ? { baselineViolations } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (!configResult.config) {
|
|
56
|
+
return {
|
|
57
|
+
t0,
|
|
58
|
+
...(diff ? { diff } : {}),
|
|
59
|
+
...(baselineViolations ? { baselineViolations } : {}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const inv1 = await runInv1Check(options.repoRoot, configResult.config, onlyFiles ? { onlyFiles } : {});
|
|
63
|
+
return {
|
|
64
|
+
t0,
|
|
65
|
+
inv1,
|
|
66
|
+
...(diff ? { diff } : {}),
|
|
67
|
+
...(baselineViolations ? { baselineViolations } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { access, cp } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/**
|
|
6
|
+
* Built-in seeds shipped inside the loopgraph package itself. The path
|
|
7
|
+
* climbs from src/cli/commands/ (or the mirrored dist/cli/commands/
|
|
8
|
+
* after build) back to <package root>/examples/<adapter>/model — both
|
|
9
|
+
* src and dist sit at the same relative depth, so this resolves
|
|
10
|
+
* correctly whether running via tsx in dev or the built dist/ output.
|
|
11
|
+
*/
|
|
12
|
+
const SEED_ROOTS = {
|
|
13
|
+
agenthub: join(__dirname, "..", "..", "..", "examples", "agenthub", "model"),
|
|
14
|
+
};
|
|
15
|
+
export async function runImport(targetDir, adapter) {
|
|
16
|
+
const seedRoot = SEED_ROOTS[adapter];
|
|
17
|
+
if (!seedRoot) {
|
|
18
|
+
const available = Object.keys(SEED_ROOTS).join(", ");
|
|
19
|
+
throw new Error(`unknown adapter "${adapter}"; available: ${available}`);
|
|
20
|
+
}
|
|
21
|
+
// dist-only publish keeps examples/ out of the package (the seed describes a
|
|
22
|
+
// target repo), so `import` works from a repo checkout but not the installed
|
|
23
|
+
// package — fail with the reason, not a bare ENOENT from cp.
|
|
24
|
+
const seedExists = await access(seedRoot)
|
|
25
|
+
.then(() => true)
|
|
26
|
+
.catch(() => false);
|
|
27
|
+
if (!seedExists) {
|
|
28
|
+
throw new Error(`the "${adapter}" seed is not bundled in this build. The published package ships the engine only; run \`loopgraph import\` from a repo checkout that carries the seed, or author your model/ directly.`);
|
|
29
|
+
}
|
|
30
|
+
const destination = join(targetDir, "model");
|
|
31
|
+
await cp(seedRoot, destination, { recursive: true });
|
|
32
|
+
return { adapter, destination };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface InitResult {
|
|
2
|
+
created: string[];
|
|
3
|
+
skipped: string[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Creates the model/ directory skeleton in a target repo. Deliberately
|
|
7
|
+
* does NOT generate a loopgraph.config.ts: Phase 0's T0 checks (schema,
|
|
8
|
+
* id-uniqueness, anchor-format, anchor-existence) don't read any config,
|
|
9
|
+
* so shipping a config file with fields nothing consumes yet would be
|
|
10
|
+
* scaffolding that lies about current capability.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runInit(targetDir: string): Promise<InitResult>;
|
|
13
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { LOOP_DISCOVERY_PROMPT, SETUP_GITHUB_ACTIONS_PROMPT, SETUP_PR_TEMPLATE_PROMPT, } from "../assets/agent-kit.js";
|
|
4
|
+
const MODEL_SUBDIRS = ["flows", "loops", "junctions", "scenarios", "baseline"];
|
|
5
|
+
/**
|
|
6
|
+
* The agent kit (Proposal 009): instruction files a coding agent in the target
|
|
7
|
+
* repo reads and executes. init deliberately ships PROMPTS, not finished
|
|
8
|
+
* workflows/templates — every repo's CI conventions differ, so the repo's own
|
|
9
|
+
* agent writes the PR template section and GitHub Actions adapted to local
|
|
10
|
+
* conventions, guided by these files.
|
|
11
|
+
*/
|
|
12
|
+
const AGENT_KIT = [
|
|
13
|
+
{ file: "loop-discovery.md", content: LOOP_DISCOVERY_PROMPT },
|
|
14
|
+
{ file: "setup-pr-template.md", content: SETUP_PR_TEMPLATE_PROMPT },
|
|
15
|
+
{ file: "setup-github-actions.md", content: SETUP_GITHUB_ACTIONS_PROMPT },
|
|
16
|
+
];
|
|
17
|
+
const MODEL_README = `# model/
|
|
18
|
+
|
|
19
|
+
Behavioral model source of truth (proposal 001 §4): \`flows/\` (Flow),
|
|
20
|
+
\`loops/\` (Loop, including embedded submachines), \`junctions/\` (Junction),
|
|
21
|
+
\`scenarios/\` (Scenario/GWT), \`baseline/\` (debt baseline).
|
|
22
|
+
|
|
23
|
+
A file may hold a single node or an array of nodes. Run \`loopgraph check\`
|
|
24
|
+
to validate this directory.
|
|
25
|
+
`;
|
|
26
|
+
/**
|
|
27
|
+
* Creates the model/ directory skeleton in a target repo. Deliberately
|
|
28
|
+
* does NOT generate a loopgraph.config.ts: Phase 0's T0 checks (schema,
|
|
29
|
+
* id-uniqueness, anchor-format, anchor-existence) don't read any config,
|
|
30
|
+
* so shipping a config file with fields nothing consumes yet would be
|
|
31
|
+
* scaffolding that lies about current capability.
|
|
32
|
+
*/
|
|
33
|
+
export async function runInit(targetDir) {
|
|
34
|
+
const created = [];
|
|
35
|
+
const skipped = [];
|
|
36
|
+
for (const sub of MODEL_SUBDIRS) {
|
|
37
|
+
await mkdir(join(targetDir, "model", sub), { recursive: true });
|
|
38
|
+
created.push(`model/${sub}/`);
|
|
39
|
+
}
|
|
40
|
+
const readmePath = join(targetDir, "model", "README.md");
|
|
41
|
+
const exists = await access(readmePath)
|
|
42
|
+
.then(() => true)
|
|
43
|
+
.catch(() => false);
|
|
44
|
+
if (exists) {
|
|
45
|
+
skipped.push("model/README.md");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await writeFile(readmePath, MODEL_README, "utf8");
|
|
49
|
+
created.push("model/README.md");
|
|
50
|
+
}
|
|
51
|
+
// Agent kit (.loopgraph/agent/): same idempotent skip-if-exists per file, so
|
|
52
|
+
// a repo can customize a kit file and re-run init without losing edits.
|
|
53
|
+
const agentDir = join(targetDir, ".loopgraph", "agent");
|
|
54
|
+
await mkdir(agentDir, { recursive: true });
|
|
55
|
+
for (const { file, content } of AGENT_KIT) {
|
|
56
|
+
const rel = `.loopgraph/agent/${file}`;
|
|
57
|
+
const kitExists = await access(join(agentDir, file))
|
|
58
|
+
.then(() => true)
|
|
59
|
+
.catch(() => false);
|
|
60
|
+
if (kitExists) {
|
|
61
|
+
skipped.push(rel);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
await writeFile(join(agentDir, file), content, "utf8");
|
|
65
|
+
created.push(rel);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { created, skipped };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface InspectOptions {
|
|
2
|
+
/** Traversal depth (default 2, proposal 001 §4.2 / 006 A5). */
|
|
3
|
+
depth?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface InspectResult {
|
|
6
|
+
rootId: string;
|
|
7
|
+
outputPath: string;
|
|
8
|
+
/** Compact stdout digest (skeletons kept, evidence/scenario prose truncated). */
|
|
9
|
+
summary: string;
|
|
10
|
+
/**
|
|
11
|
+
* Set when a pre-existing side-channel file for this id was stale vs the
|
|
12
|
+
* current model — the Decision 004 技术点 4 consumer. See writeSideChannel.
|
|
13
|
+
*/
|
|
14
|
+
staleWarning?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `loopgraph inspect <node-id>`: slice the model around one node and write a
|
|
18
|
+
* staleness-stamped side-channel file, returning a compact summary for stdout
|
|
19
|
+
* (proposal 001 §4.2 shape: summary + workspace file path). Throws on an
|
|
20
|
+
* unknown id or unparseable model — the CLI turns those into exit code 1.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runInspect(targetDir: string, nodeId: string, options?: InspectOptions): Promise<InspectResult>;
|
|
23
|
+
//# sourceMappingURL=inspect.d.ts.map
|