specdo 1.0.2
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/CHANGELOG.md +139 -0
- package/README.md +308 -0
- package/README.zh-CN.md +306 -0
- package/bin/specdo.js +3 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +297 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/_shared.d.ts +45 -0
- package/dist/commands/_shared.d.ts.map +1 -0
- package/dist/commands/_shared.js +124 -0
- package/dist/commands/_shared.js.map +1 -0
- package/dist/commands/apply.d.ts +30 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +393 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/archive.d.ts +25 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +362 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/doctor.d.ts +21 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +180 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/domains.d.ts +14 -0
- package/dist/commands/domains.d.ts.map +1 -0
- package/dist/commands/domains.js +107 -0
- package/dist/commands/domains.js.map +1 -0
- package/dist/commands/explore.d.ts +48 -0
- package/dist/commands/explore.d.ts.map +1 -0
- package/dist/commands/explore.js +378 -0
- package/dist/commands/explore.js.map +1 -0
- package/dist/commands/init.d.ts +45 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +243 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +23 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +135 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/propose.d.ts +22 -0
- package/dist/commands/propose.d.ts.map +1 -0
- package/dist/commands/propose.js +316 -0
- package/dist/commands/propose.js.map +1 -0
- package/dist/commands/show.d.ts +15 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +214 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +146 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +21 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +113 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/validate.d.ts +117 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +446 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/core/apply-brief-renderer.d.ts +35 -0
- package/dist/core/apply-brief-renderer.d.ts.map +1 -0
- package/dist/core/apply-brief-renderer.js +242 -0
- package/dist/core/apply-brief-renderer.js.map +1 -0
- package/dist/core/config-store.d.ts +190 -0
- package/dist/core/config-store.d.ts.map +1 -0
- package/dist/core/config-store.js +280 -0
- package/dist/core/config-store.js.map +1 -0
- package/dist/core/context-store.d.ts +96 -0
- package/dist/core/context-store.d.ts.map +1 -0
- package/dist/core/context-store.js +426 -0
- package/dist/core/context-store.js.map +1 -0
- package/dist/core/json-schemas.d.ts +349 -0
- package/dist/core/json-schemas.d.ts.map +1 -0
- package/dist/core/json-schemas.js +125 -0
- package/dist/core/json-schemas.js.map +1 -0
- package/dist/core/skill-content/cross-domain.d.ts +12 -0
- package/dist/core/skill-content/cross-domain.d.ts.map +1 -0
- package/dist/core/skill-content/cross-domain.js +291 -0
- package/dist/core/skill-content/cross-domain.js.map +1 -0
- package/dist/core/skill-content/protocol-examples.d.ts +13 -0
- package/dist/core/skill-content/protocol-examples.d.ts.map +1 -0
- package/dist/core/skill-content/protocol-examples.js +190 -0
- package/dist/core/skill-content/protocol-examples.js.map +1 -0
- package/dist/core/skill-content/workflow-content.d.ts +25 -0
- package/dist/core/skill-content/workflow-content.d.ts.map +1 -0
- package/dist/core/skill-content/workflow-content.js +1572 -0
- package/dist/core/skill-content/workflow-content.js.map +1 -0
- package/dist/core/skill-exporter.d.ts +186 -0
- package/dist/core/skill-exporter.d.ts.map +1 -0
- package/dist/core/skill-exporter.js +922 -0
- package/dist/core/skill-exporter.js.map +1 -0
- package/dist/core/spec-sync.d.ts +65 -0
- package/dist/core/spec-sync.d.ts.map +1 -0
- package/dist/core/spec-sync.js +226 -0
- package/dist/core/spec-sync.js.map +1 -0
- package/dist/core/task-parser.d.ts +58 -0
- package/dist/core/task-parser.d.ts.map +1 -0
- package/dist/core/task-parser.js +244 -0
- package/dist/core/task-parser.js.map +1 -0
- package/dist/core/template-renderer.d.ts +51 -0
- package/dist/core/template-renderer.d.ts.map +1 -0
- package/dist/core/template-renderer.js +362 -0
- package/dist/core/template-renderer.js.map +1 -0
- package/dist/domains/architecture.d.ts +34 -0
- package/dist/domains/architecture.d.ts.map +1 -0
- package/dist/domains/architecture.js +341 -0
- package/dist/domains/architecture.js.map +1 -0
- package/dist/domains/backend.d.ts +35 -0
- package/dist/domains/backend.d.ts.map +1 -0
- package/dist/domains/backend.js +367 -0
- package/dist/domains/backend.js.map +1 -0
- package/dist/domains/frontend.d.ts +36 -0
- package/dist/domains/frontend.d.ts.map +1 -0
- package/dist/domains/frontend.js +373 -0
- package/dist/domains/frontend.js.map +1 -0
- package/dist/domains/index.d.ts +49 -0
- package/dist/domains/index.d.ts.map +1 -0
- package/dist/domains/index.js +255 -0
- package/dist/domains/index.js.map +1 -0
- package/dist/domains/operations.d.ts +37 -0
- package/dist/domains/operations.d.ts.map +1 -0
- package/dist/domains/operations.js +344 -0
- package/dist/domains/operations.js.map +1 -0
- package/dist/domains/pool-ranking.d.ts +43 -0
- package/dist/domains/pool-ranking.d.ts.map +1 -0
- package/dist/domains/pool-ranking.js +153 -0
- package/dist/domains/pool-ranking.js.map +1 -0
- package/dist/domains/quality.d.ts +45 -0
- package/dist/domains/quality.d.ts.map +1 -0
- package/dist/domains/quality.js +368 -0
- package/dist/domains/quality.js.map +1 -0
- package/dist/domains/security.d.ts +19 -0
- package/dist/domains/security.d.ts.map +1 -0
- package/dist/domains/security.js +364 -0
- package/dist/domains/security.js.map +1 -0
- package/dist/domains/signal-match.d.ts +25 -0
- package/dist/domains/signal-match.d.ts.map +1 -0
- package/dist/domains/signal-match.js +67 -0
- package/dist/domains/signal-match.js.map +1 -0
- package/dist/domains/types.d.ts +354 -0
- package/dist/domains/types.d.ts.map +1 -0
- package/dist/domains/types.js +12 -0
- package/dist/domains/types.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/protocols/index.d.ts +36 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +85 -0
- package/dist/protocols/index.js.map +1 -0
- package/dist/protocols/review-to-solid.d.ts +32 -0
- package/dist/protocols/review-to-solid.d.ts.map +1 -0
- package/dist/protocols/review-to-solid.js +309 -0
- package/dist/protocols/review-to-solid.js.map +1 -0
- package/dist/utils/prompt.d.ts +37 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +81 -0
- package/dist/utils/prompt.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* specdo domains [list | show <name>] [--json]
|
|
3
|
+
*
|
|
4
|
+
* 默认 `domains list`。`show <name>` 展示 override 应用后的有效配置。
|
|
5
|
+
*/
|
|
6
|
+
import { loadConfig, resolveDomains, getResolvedDomain } from '../core/config-store.js';
|
|
7
|
+
import { extractQuestions } from '../domains/index.js';
|
|
8
|
+
import { fail, findSpecdoConfig, ok, failOnInvalidConfig, } from './_shared.js';
|
|
9
|
+
export async function handleDomains(options) {
|
|
10
|
+
const { cwd } = options;
|
|
11
|
+
if (!findSpecdoConfig(cwd)) {
|
|
12
|
+
return fail(1, ["Not initialized. Run 'specdo init' first."]);
|
|
13
|
+
}
|
|
14
|
+
const sub = options.subcommand ?? 'list';
|
|
15
|
+
const configResult = loadConfig(cwd);
|
|
16
|
+
const configFailure = failOnInvalidConfig(configResult);
|
|
17
|
+
if (configFailure)
|
|
18
|
+
return configFailure;
|
|
19
|
+
const { config } = configResult;
|
|
20
|
+
if (sub === 'show') {
|
|
21
|
+
if (!options.name) {
|
|
22
|
+
return fail(1, ['domains show requires <name>. Example: specdo domains show security']);
|
|
23
|
+
}
|
|
24
|
+
const resolved = getResolvedDomain(config, options.name);
|
|
25
|
+
if (!resolved) {
|
|
26
|
+
return fail(1, [`Unknown domain '${options.name}'.`]);
|
|
27
|
+
}
|
|
28
|
+
if (options.json) {
|
|
29
|
+
return ok([JSON.stringify(resolved, null, 2)]);
|
|
30
|
+
}
|
|
31
|
+
const stdout = [];
|
|
32
|
+
const d = resolved.domain;
|
|
33
|
+
stdout.push(`# ${d.name}`);
|
|
34
|
+
stdout.push('');
|
|
35
|
+
stdout.push(d.description);
|
|
36
|
+
stdout.push('');
|
|
37
|
+
stdout.push('## Override summary');
|
|
38
|
+
stdout.push('');
|
|
39
|
+
for (const c of resolved.overrideCounts) {
|
|
40
|
+
const flags = [
|
|
41
|
+
c.enabled ? 'enabled' : 'DISABLED',
|
|
42
|
+
c.appended > 0 ? `+${c.appended}` : null,
|
|
43
|
+
c.prepended > 0 ? `^${c.prepended}` : null,
|
|
44
|
+
c.replaced > 0 ? `~${c.replaced}` : null,
|
|
45
|
+
c.patternsAdded > 0 ? `patterns+${c.patternsAdded}` : null,
|
|
46
|
+
].filter(Boolean).join(' ');
|
|
47
|
+
stdout.push(` - ${c.stage.padEnd(10)} ${flags}`);
|
|
48
|
+
}
|
|
49
|
+
stdout.push('');
|
|
50
|
+
if (d.explore) {
|
|
51
|
+
stdout.push(`## explore.signals (${d.explore.signals.length})`);
|
|
52
|
+
stdout.push('');
|
|
53
|
+
stdout.push(' ' + d.explore.signals.slice(0, 12).join(', ') + (d.explore.signals.length > 12 ? ', …' : ''));
|
|
54
|
+
stdout.push('');
|
|
55
|
+
const questions = extractQuestions(d.explore.questions);
|
|
56
|
+
stdout.push(`## explore.questions (${questions.length})`);
|
|
57
|
+
for (const q of questions)
|
|
58
|
+
stdout.push(` - ${q}`);
|
|
59
|
+
stdout.push('');
|
|
60
|
+
}
|
|
61
|
+
if (d.design) {
|
|
62
|
+
stdout.push(`## design.checklist (${d.design.checklist.length})`);
|
|
63
|
+
for (const c of d.design.checklist)
|
|
64
|
+
stdout.push(` - [ ] ${c}`);
|
|
65
|
+
stdout.push('');
|
|
66
|
+
}
|
|
67
|
+
if (d.implement) {
|
|
68
|
+
stdout.push(`## implement.focusAreas (${d.implement.focusAreas.length})`);
|
|
69
|
+
for (const f of d.implement.focusAreas)
|
|
70
|
+
stdout.push(` - ${f}`);
|
|
71
|
+
stdout.push('');
|
|
72
|
+
}
|
|
73
|
+
if (d.verify) {
|
|
74
|
+
stdout.push(`## verify.checklist (${d.verify.checklist.length})`);
|
|
75
|
+
for (const v of d.verify.checklist)
|
|
76
|
+
stdout.push(` - [ ] ${v}`);
|
|
77
|
+
stdout.push('');
|
|
78
|
+
}
|
|
79
|
+
return ok(stdout);
|
|
80
|
+
}
|
|
81
|
+
// list
|
|
82
|
+
const all = resolveDomains(config);
|
|
83
|
+
if (options.json) {
|
|
84
|
+
const payload = all.map((r) => ({
|
|
85
|
+
name: r.domain.name,
|
|
86
|
+
description: r.domain.description,
|
|
87
|
+
hasDisabledStages: r.hasDisabledStages,
|
|
88
|
+
overrideCounts: r.overrideCounts,
|
|
89
|
+
}));
|
|
90
|
+
return ok([JSON.stringify({ kind: 'domains', entries: payload }, null, 2)]);
|
|
91
|
+
}
|
|
92
|
+
const stdout = [];
|
|
93
|
+
stdout.push(`Available domains (${all.length}):`);
|
|
94
|
+
stdout.push('');
|
|
95
|
+
stdout.push(` ${'NAME'.padEnd(14)} ${'OVERRIDES'.padEnd(34)} DESCRIPTION`);
|
|
96
|
+
for (const r of all) {
|
|
97
|
+
const overrideHint = r.overrideCounts
|
|
98
|
+
.filter((c) => c.appended + c.prepended + c.replaced + c.patternsAdded > 0 || !c.enabled)
|
|
99
|
+
.map((c) => `${c.stage}${!c.enabled ? '✗' : ''}+${c.appended + c.prepended}${c.patternsAdded > 0 ? `/p${c.patternsAdded}` : ''}`)
|
|
100
|
+
.join(' ');
|
|
101
|
+
stdout.push(` ${r.domain.name.padEnd(14)} ${(overrideHint || '(none)').padEnd(34)} ${r.domain.description}`);
|
|
102
|
+
}
|
|
103
|
+
stdout.push('');
|
|
104
|
+
stdout.push('Use `specdo domains show <name>` for the resolved checklist after overrides.');
|
|
105
|
+
return ok(stdout);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=domains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/commands/domains.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* specdo explore [idea] [options]
|
|
3
|
+
*
|
|
4
|
+
* 行为:
|
|
5
|
+
* 1. 必须 init 之后调用(specdo/config.yaml 存在)
|
|
6
|
+
* 2. idea 为空或纯空白 → 必须提供 --change 才能继续
|
|
7
|
+
* 3. 推导 change name:优先 --change > slug(idea)
|
|
8
|
+
* 4. 创建 specdo/changes/<name>/(已存在且非该次 explore 创建则提示)
|
|
9
|
+
* 5. scoreDomains(idea, active explore domains resolved from config)
|
|
10
|
+
* 6. 收集回答:交互模式逐题问答;非交互或 --answers 直接读 JSON
|
|
11
|
+
* 7. 写入 context.json: explore stage
|
|
12
|
+
*
|
|
13
|
+
* 不变量:
|
|
14
|
+
* - 任何步骤失败必须保留磁盘上的 last-good 状态
|
|
15
|
+
* - --answers 与 --non-interactive 互不强制;仅 --answers 也跳过交互
|
|
16
|
+
*/
|
|
17
|
+
import type { DomainMatch } from '../domains/types.js';
|
|
18
|
+
import { CommandResult } from './_shared.js';
|
|
19
|
+
export interface ExploreOptions {
|
|
20
|
+
cwd: string;
|
|
21
|
+
idea?: string;
|
|
22
|
+
change?: string;
|
|
23
|
+
nonInteractive?: boolean;
|
|
24
|
+
answers?: string;
|
|
25
|
+
/** agent 显式指定的领域列表(逗号分隔);传入后旁路 scoreDomains() */
|
|
26
|
+
domains?: string;
|
|
27
|
+
/** agent 收集的自由文本上下文 */
|
|
28
|
+
context?: string;
|
|
29
|
+
/** agent 自评的 explore 深度 */
|
|
30
|
+
depth?: 'shallow' | 'standard' | 'deep';
|
|
31
|
+
/** 注入项 — 测试时可替换 */
|
|
32
|
+
now?: () => string;
|
|
33
|
+
/** 注入项 — 测试时可替换交互问答 */
|
|
34
|
+
promptFn?: (question: string) => Promise<string>;
|
|
35
|
+
/** 注入项 — 测试时可替换 isNonInteractive 判断 */
|
|
36
|
+
isNonInteractiveFn?: () => boolean;
|
|
37
|
+
/** 启用 LLM 驱动的问题生成(agent 模式) */
|
|
38
|
+
llm?: boolean;
|
|
39
|
+
/** Agent 预生成的 LLM 问题 JSON: Record<string, string[]> */
|
|
40
|
+
llmQuestions?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ExploreResult extends CommandResult {
|
|
43
|
+
/** 计算出的 change name;用于测试断言 */
|
|
44
|
+
changeName?: string;
|
|
45
|
+
matchedDomains?: DomainMatch[];
|
|
46
|
+
}
|
|
47
|
+
export declare function handleExplore(options: ExploreOptions): Promise<ExploreResult>;
|
|
48
|
+
//# sourceMappingURL=explore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../src/commands/explore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIrE,OAAO,EACL,aAAa,EAQd,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACxC,mBAAmB;IACnB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,+BAA+B;IAC/B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,WAAW,EAAE,CAAC;CAChC;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAqUnF"}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* specdo explore [idea] [options]
|
|
3
|
+
*
|
|
4
|
+
* 行为:
|
|
5
|
+
* 1. 必须 init 之后调用(specdo/config.yaml 存在)
|
|
6
|
+
* 2. idea 为空或纯空白 → 必须提供 --change 才能继续
|
|
7
|
+
* 3. 推导 change name:优先 --change > slug(idea)
|
|
8
|
+
* 4. 创建 specdo/changes/<name>/(已存在且非该次 explore 创建则提示)
|
|
9
|
+
* 5. scoreDomains(idea, active explore domains resolved from config)
|
|
10
|
+
* 6. 收集回答:交互模式逐题问答;非交互或 --answers 直接读 JSON
|
|
11
|
+
* 7. 写入 context.json: explore stage
|
|
12
|
+
*
|
|
13
|
+
* 不变量:
|
|
14
|
+
* - 任何步骤失败必须保留磁盘上的 last-good 状态
|
|
15
|
+
* - --answers 与 --non-interactive 互不强制;仅 --answers 也跳过交互
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from 'node:fs';
|
|
18
|
+
import { scoreDomains, rankQuestions, isQuestionPool } from '../domains/index.js';
|
|
19
|
+
import { ContextStore } from '../core/context-store.js';
|
|
20
|
+
import { loadConfig, resolveDomains } from '../core/config-store.js';
|
|
21
|
+
import { askQuestion, isNonInteractive, parseAnswersJson } from '../utils/prompt.js';
|
|
22
|
+
import { fail, findSpecdoConfig, generateChangeSlug, getChangeDir, isValidChangeName, safeIsDir, failOnInvalidConfig, } from './_shared.js';
|
|
23
|
+
export async function handleExplore(options) {
|
|
24
|
+
const { cwd } = options;
|
|
25
|
+
const idea = (options.idea ?? '').trim();
|
|
26
|
+
const errOutput = [];
|
|
27
|
+
// 1. 前置检查
|
|
28
|
+
if (!findSpecdoConfig(cwd)) {
|
|
29
|
+
return fail(1, ["Not initialized. Run 'specdo init' first."]);
|
|
30
|
+
}
|
|
31
|
+
// 2. 推导 change name
|
|
32
|
+
let changeName;
|
|
33
|
+
if (options.change) {
|
|
34
|
+
if (!isValidChangeName(options.change)) {
|
|
35
|
+
return fail(1, [
|
|
36
|
+
`Invalid --change name: "${options.change}". Use lowercase ASCII, digits, and hyphens (1-50 chars, no leading/trailing hyphen).`,
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
changeName = options.change;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (!idea) {
|
|
43
|
+
return fail(1, ['Please provide a change description or use --change <name>.']);
|
|
44
|
+
}
|
|
45
|
+
let slug = generateChangeSlug(idea);
|
|
46
|
+
if (!slug) {
|
|
47
|
+
// Non-ASCII input (e.g. CJK) produces no usable tokens.
|
|
48
|
+
// Fall back to a stable timestamp-based name — user can override with --change.
|
|
49
|
+
slug = `change-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
50
|
+
}
|
|
51
|
+
changeName = slug;
|
|
52
|
+
}
|
|
53
|
+
const changeDir = getChangeDir(cwd, changeName);
|
|
54
|
+
// 3. 目录已存在 → 仅在用户未提供 --change 时报错;用户显式提供则允许复用
|
|
55
|
+
if (safeIsDir(changeDir) && !options.change) {
|
|
56
|
+
return fail(1, [
|
|
57
|
+
`Change '${changeName}' already exists at ${changeDir}.`,
|
|
58
|
+
`Use a different idea or pass --change <name> to reuse.`,
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
// 4. domain matching — use resolved domains so config.overrides affects
|
|
62
|
+
// explore signals/questions just like later propose/apply/archive stages.
|
|
63
|
+
const configResult = loadConfig(cwd);
|
|
64
|
+
const configFailure = failOnInvalidConfig(configResult);
|
|
65
|
+
if (configFailure)
|
|
66
|
+
return configFailure;
|
|
67
|
+
const resolvedDomains = resolveDomains(configResult.config).map((r) => r.domain);
|
|
68
|
+
const exploreDomains = resolvedDomains.filter((domain) => domain.explore);
|
|
69
|
+
let matched;
|
|
70
|
+
let matchedDomains;
|
|
71
|
+
let domainSelectionMode;
|
|
72
|
+
if (options.domains) {
|
|
73
|
+
// Agent 驱动模式:解析 --domains,校验每个领域名存在
|
|
74
|
+
const names = options.domains.split(',').map((s) => s.trim()).filter(Boolean);
|
|
75
|
+
const unknown = names.find((n) => !exploreDomains.some((d) => d.name === n));
|
|
76
|
+
if (unknown) {
|
|
77
|
+
return fail(2, [
|
|
78
|
+
`Unknown domain: "${unknown}".`,
|
|
79
|
+
`Available domains: ${exploreDomains.map((d) => d.name).join(', ')}.`,
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
matched = names.map((name) => ({
|
|
83
|
+
name,
|
|
84
|
+
score: 1.0,
|
|
85
|
+
reason: 'Agent-selected via --domains',
|
|
86
|
+
}));
|
|
87
|
+
matchedDomains = exploreDomains.filter((d) => names.includes(d.name));
|
|
88
|
+
domainSelectionMode = 'agent';
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// 回退到 scoreDomains()(向后兼容)
|
|
92
|
+
matched = idea ? scoreDomains(idea, exploreDomains) : [];
|
|
93
|
+
matchedDomains = matched
|
|
94
|
+
.map((m) => exploreDomains.find((d) => d.name === m.name))
|
|
95
|
+
.filter((d) => Boolean(d));
|
|
96
|
+
domainSelectionMode = 'auto';
|
|
97
|
+
}
|
|
98
|
+
// 4b. LLM question generation / ingestion
|
|
99
|
+
let llmQuestions;
|
|
100
|
+
let llmQuestionsOrigin;
|
|
101
|
+
if (options.llm && !options.llmQuestions) {
|
|
102
|
+
// Phase 1: persist a checkpoint so the agent can re-run with --llm-questions.
|
|
103
|
+
// The explore stage is marked in-progress (completedAt=null) — the second
|
|
104
|
+
// call completes it after LLM questions are injected.
|
|
105
|
+
try {
|
|
106
|
+
fs.mkdirSync(changeDir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return fail(3, [`Failed to create change directory: ${err.message}`]);
|
|
110
|
+
}
|
|
111
|
+
const now = options.now ?? (() => new Date().toISOString());
|
|
112
|
+
const store = new ContextStore({ changeDir, now });
|
|
113
|
+
try {
|
|
114
|
+
store.writeDeep({
|
|
115
|
+
change: { name: changeName },
|
|
116
|
+
explore: {
|
|
117
|
+
input: idea,
|
|
118
|
+
matchedDomains: matched,
|
|
119
|
+
flaggedConcerns: detectConcerns({}, idea),
|
|
120
|
+
collectedContext: options.context ?? '',
|
|
121
|
+
depth: null, // determined on the --llm-questions re-run
|
|
122
|
+
completedAt: null, // in-progress — will be set on the --llm-questions re-run
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
return fail(3, [`Failed to persist context.json: ${err.message}`]);
|
|
128
|
+
}
|
|
129
|
+
// Build enriched LLM prompt with pool metadata
|
|
130
|
+
const llmRequest = {
|
|
131
|
+
action: 'GENERATE_QUESTIONS',
|
|
132
|
+
payload: {
|
|
133
|
+
idea,
|
|
134
|
+
changeName,
|
|
135
|
+
domains: matchedDomains.map((d) => {
|
|
136
|
+
const rawQuestions = d.explore?.questions ?? [];
|
|
137
|
+
const poolItems = isQuestionPool(rawQuestions)
|
|
138
|
+
? rawQuestions.items.map((item) => ({
|
|
139
|
+
text: item.text,
|
|
140
|
+
id: item.id ?? null,
|
|
141
|
+
priority: item.priority,
|
|
142
|
+
signals: item.signals.slice(0, 8),
|
|
143
|
+
requiresAnswer: item.requiresAnswer?.length ? item.requiresAnswer : undefined,
|
|
144
|
+
conditional: item.conditional && Object.keys(item.conditional).length > 0 ? item.conditional : undefined,
|
|
145
|
+
}))
|
|
146
|
+
: [];
|
|
147
|
+
return {
|
|
148
|
+
name: d.name,
|
|
149
|
+
description: d.description,
|
|
150
|
+
signals: d.explore?.signals ?? [],
|
|
151
|
+
questionPool: {
|
|
152
|
+
itemCount: poolItems.length,
|
|
153
|
+
defaultCount: isQuestionPool(rawQuestions) ? rawQuestions.defaultCount : 0,
|
|
154
|
+
items: poolItems,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}),
|
|
158
|
+
existingAnswers: {},
|
|
159
|
+
instruction: 'Generate 5-10 clarifying questions per domain. Questions must be specific to the idea and domain signal taxonomy. Return JSON: {"<domain>":["Q1?","Q2?",...]}',
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
const stdout = [];
|
|
163
|
+
stdout.push(`✓ Checkpoint saved: ${changeName}`);
|
|
164
|
+
stdout.push('');
|
|
165
|
+
stdout.push('Matched domains:');
|
|
166
|
+
for (const d of matched) {
|
|
167
|
+
stdout.push(` - ${d.name.padEnd(14)} score=${d.score.toFixed(3)} ${d.reason}`);
|
|
168
|
+
}
|
|
169
|
+
stdout.push('');
|
|
170
|
+
stdout.push('---SPECDO_LLM_PROMPT---');
|
|
171
|
+
stdout.push(JSON.stringify(llmRequest, null, 2));
|
|
172
|
+
stdout.push('---END_LLM_PROMPT---');
|
|
173
|
+
stdout.push('');
|
|
174
|
+
if (options.answers !== undefined) {
|
|
175
|
+
stdout.push('');
|
|
176
|
+
stdout.push('Note: --answers is ignored during the LLM checkpoint phase. Answers will be consumed when re-running with --llm-questions.');
|
|
177
|
+
}
|
|
178
|
+
stdout.push('Next: specdo explore \'<idea>\' --change ' + changeName + ' --domains ' + matchedDomains.map((d) => d.name).join(',') + ' --llm-questions \'<json>\'');
|
|
179
|
+
return {
|
|
180
|
+
exitCode: 0,
|
|
181
|
+
stdout,
|
|
182
|
+
stderr: [],
|
|
183
|
+
changeName,
|
|
184
|
+
matchedDomains: matched,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (options.llmQuestions) {
|
|
188
|
+
const MAX_LLM_QUESTIONS_BYTES = 100_000; // 100 KB
|
|
189
|
+
if (options.llmQuestions.length > MAX_LLM_QUESTIONS_BYTES) {
|
|
190
|
+
return fail(2, [`--llm-questions payload too large (${options.llmQuestions.length} bytes; max ${MAX_LLM_QUESTIONS_BYTES})`]);
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(options.llmQuestions);
|
|
194
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
195
|
+
return fail(2, ['--llm-questions must be a JSON object mapping domain names to string arrays']);
|
|
196
|
+
}
|
|
197
|
+
for (const [domain, qs] of Object.entries(parsed)) {
|
|
198
|
+
if (!Array.isArray(qs) || qs.some((q) => typeof q !== 'string' || q.trim().length === 0 || q.length > 2000)) {
|
|
199
|
+
return fail(2, [`--llm-questions: value for "${domain}" must be non-empty strings with at most 2000 characters`]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
llmQuestions = parsed;
|
|
203
|
+
llmQuestionsOrigin = 'provided via --llm-questions';
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
return fail(2, [`Invalid --llm-questions JSON: ${err.message}`]);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// 5. 解析 --answers 或 收集回答
|
|
210
|
+
let answers = {};
|
|
211
|
+
let answersOriginRationale = null;
|
|
212
|
+
if (options.answers !== undefined) {
|
|
213
|
+
try {
|
|
214
|
+
answers = parseAnswersJson(options.answers);
|
|
215
|
+
answersOriginRationale = 'preset (from --answers JSON)';
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
return fail(1, [err.message]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (options.nonInteractive ?? options.isNonInteractiveFn?.() ?? isNonInteractive()) {
|
|
222
|
+
// 非交互模式但无 --answers → 跳过问答阶段
|
|
223
|
+
answersOriginRationale = 'skipped (non-interactive mode without --answers)';
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
const promptFn = options.promptFn ?? ((q) => askQuestion(q));
|
|
227
|
+
for (const domain of matchedDomains) {
|
|
228
|
+
const domainLlmQs = llmQuestions?.[domain.name];
|
|
229
|
+
if (domainLlmQs && domainLlmQs.length > 0) {
|
|
230
|
+
// Use LLM-generated questions
|
|
231
|
+
for (let i = 0; i < domainLlmQs.length; i++) {
|
|
232
|
+
const key = `${domain.name}:${i}`;
|
|
233
|
+
const answer = await promptFn(`[${domain.name}] ${domainLlmQs[i]}`);
|
|
234
|
+
answers[key] = answer;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// Progressive re-ranking pool loop: re-rank after each answer so
|
|
239
|
+
// dependency chains and conditionals can unlock follow-up questions.
|
|
240
|
+
const questions = domain.explore?.questions ?? [];
|
|
241
|
+
const maxCount = isQuestionPool(questions)
|
|
242
|
+
? questions.defaultCount
|
|
243
|
+
: (Array.isArray(questions) ? questions.length : 8);
|
|
244
|
+
const askedKeys = new Set();
|
|
245
|
+
let askedCount = 0;
|
|
246
|
+
while (askedCount < maxCount) {
|
|
247
|
+
const ranked = rankQuestions(questions, {
|
|
248
|
+
input: idea,
|
|
249
|
+
existingAnswers: answers,
|
|
250
|
+
});
|
|
251
|
+
const next = ranked.find((rq) => !askedKeys.has(rq.answerKey));
|
|
252
|
+
if (!next)
|
|
253
|
+
break; // no remaining eligible questions
|
|
254
|
+
askedKeys.add(next.answerKey);
|
|
255
|
+
askedCount++;
|
|
256
|
+
const key = `${domain.name}:${next.answerKey}`;
|
|
257
|
+
const answer = await promptFn(`[${domain.name}] ${next.text}`);
|
|
258
|
+
answers[key] = answer;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
answersOriginRationale = llmQuestions
|
|
263
|
+
? `collected interactively (LLM-generated questions; ${llmQuestionsOrigin})`
|
|
264
|
+
: 'collected interactively (pool ranked)';
|
|
265
|
+
}
|
|
266
|
+
// Warn if --llm-questions contained keys for unmatched domains
|
|
267
|
+
if (llmQuestions) {
|
|
268
|
+
const matchedNames = new Set(matchedDomains.map((d) => d.name));
|
|
269
|
+
const unused = Object.keys(llmQuestions).filter((n) => !matchedNames.has(n));
|
|
270
|
+
if (unused.length > 0) {
|
|
271
|
+
errOutput.push(`specdo: warning: --llm-questions includes questions for unmatched domain(s): ${unused.join(', ')}. ` +
|
|
272
|
+
`These questions were ignored. Valid domains: ${matchedDomains.map((d) => d.name).join(', ')}.`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// 6. 创建目录 + 写 context.json
|
|
276
|
+
try {
|
|
277
|
+
fs.mkdirSync(changeDir, { recursive: true });
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
return fail(3, [`Failed to create change directory: ${err.message}`]);
|
|
281
|
+
}
|
|
282
|
+
const now = options.now ?? (() => new Date().toISOString());
|
|
283
|
+
const store = new ContextStore({ changeDir, now });
|
|
284
|
+
const flaggedConcerns = detectConcerns(answers, idea);
|
|
285
|
+
const resolvedDepth = options.depth ?? resolveDefaultDepth(options);
|
|
286
|
+
try {
|
|
287
|
+
store.writeDeep({
|
|
288
|
+
change: { name: changeName },
|
|
289
|
+
explore: {
|
|
290
|
+
input: idea,
|
|
291
|
+
matchedDomains: matched,
|
|
292
|
+
answers,
|
|
293
|
+
flaggedConcerns,
|
|
294
|
+
collectedContext: options.context ?? '',
|
|
295
|
+
depth: resolvedDepth,
|
|
296
|
+
completedAt: now(),
|
|
297
|
+
...(llmQuestions ? { llmQuestions } : {}),
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
return fail(3, [`Failed to persist context.json: ${err.message}`]);
|
|
303
|
+
}
|
|
304
|
+
// 7. 输出
|
|
305
|
+
const stdout = [];
|
|
306
|
+
stdout.push(`✓ Change captured: ${changeName}`);
|
|
307
|
+
stdout.push('');
|
|
308
|
+
if (matched.length === 0) {
|
|
309
|
+
if (domainSelectionMode === 'auto') {
|
|
310
|
+
stdout.push('No domains auto-matched from idea text. Consider specifying domains explicitly:');
|
|
311
|
+
stdout.push(' specdo explore \'<idea>\' --domains <list> [--context \'<text>\']');
|
|
312
|
+
stdout.push(`Available domains: ${exploreDomains.map((d) => d.name).join(', ')}.`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
stdout.push('No domains selected.');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
stdout.push(`Matched domains (${domainSelectionMode === 'agent' ? 'agent-selected' : 'by score'}):`);
|
|
320
|
+
for (const m of matched) {
|
|
321
|
+
stdout.push(` - ${m.name.padEnd(14)} score=${m.score.toFixed(3)} ${m.reason}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
stdout.push('');
|
|
325
|
+
stdout.push(`Answers: ${answersOriginRationale ?? 'none'} (${Object.keys(answers).length} entries).`);
|
|
326
|
+
if (options.answers && !options.nonInteractive) {
|
|
327
|
+
stdout.push('Note: --answers provided without --non-interactive; interactive prompts have been skipped.');
|
|
328
|
+
}
|
|
329
|
+
if (flaggedConcerns.length > 0) {
|
|
330
|
+
stdout.push('');
|
|
331
|
+
stdout.push('Flagged concerns:');
|
|
332
|
+
for (const concern of flaggedConcerns) {
|
|
333
|
+
stdout.push(` ! ${concern}`);
|
|
334
|
+
}
|
|
335
|
+
stdout.push('These concerns will be highlighted during propose and apply phases.');
|
|
336
|
+
}
|
|
337
|
+
stdout.push('');
|
|
338
|
+
stdout.push('Next: specdo propose --change ' + changeName);
|
|
339
|
+
return { exitCode: 0, stdout, stderr: errOutput, changeName, matchedDomains: matched };
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* 当 --depth 未提供时自动推导 explore 深度:
|
|
343
|
+
* - 同时有 domains + context → standard
|
|
344
|
+
* - 否则 → shallow
|
|
345
|
+
*/
|
|
346
|
+
function resolveDefaultDepth(opts) {
|
|
347
|
+
if (opts.domains && opts.context && opts.context.trim().length > 0)
|
|
348
|
+
return 'standard';
|
|
349
|
+
if (opts.answers) {
|
|
350
|
+
try {
|
|
351
|
+
const a = JSON.parse(opts.answers);
|
|
352
|
+
if (a && typeof a === 'object' && !Array.isArray(a) && Object.keys(a).length >= 3)
|
|
353
|
+
return 'standard';
|
|
354
|
+
}
|
|
355
|
+
catch { /* invalid JSON — fall through */ }
|
|
356
|
+
}
|
|
357
|
+
return 'shallow';
|
|
358
|
+
}
|
|
359
|
+
const CONCERN_KEYWORDS = [
|
|
360
|
+
'breaking change', 'backward compat', 'backwards compat',
|
|
361
|
+
'security risk', 'security concern', 'vulnerability',
|
|
362
|
+
'performance risk', 'scalability concern', 'data loss',
|
|
363
|
+
'migration risk', 'downtime', 'outage',
|
|
364
|
+
'compliance', 'regulatory', 'gdpr', 'pci',
|
|
365
|
+
'no test', 'untested', 'no coverage',
|
|
366
|
+
'deadline', 'time constraint', 'urgent',
|
|
367
|
+
];
|
|
368
|
+
function detectConcerns(answers, idea) {
|
|
369
|
+
const allText = [idea, ...Object.values(answers)].join(' ').toLowerCase();
|
|
370
|
+
const flagged = [];
|
|
371
|
+
for (const keyword of CONCERN_KEYWORDS) {
|
|
372
|
+
if (allText.includes(keyword)) {
|
|
373
|
+
flagged.push(keyword);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return [...new Set(flagged)];
|
|
377
|
+
}
|
|
378
|
+
//# sourceMappingURL=explore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explore.js","sourceRoot":"","sources":["../../src/commands/explore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* specdo init <agent> [-g] [-y]
|
|
3
|
+
*
|
|
4
|
+
* agent 是必传位置参数。运行流程(更严格的契约):
|
|
5
|
+
* 1. agent 缺失 → exit 2,不做任何事
|
|
6
|
+
* 2. 解析 agent;任意失败('all' / typo / unknown)→ exit 2,不创建骨架
|
|
7
|
+
* 3. agent 完全合法后才创建/修复骨架
|
|
8
|
+
* 4. 导出 skill
|
|
9
|
+
*
|
|
10
|
+
* 关键不变量:
|
|
11
|
+
* - 仅当 agent 完全合法时,骨架才被创建(agent 错误不留下任何痕迹)
|
|
12
|
+
* - config.yaml 永不覆盖(仅缺失时写入)
|
|
13
|
+
* - 错误提示不引导用户去手填 'universal';交互层(cli/index.ts)会在
|
|
14
|
+
* 未知无近似词时主动 prompt 是否走通用目录
|
|
15
|
+
* - 任何 FS 写入失败 → exit 3
|
|
16
|
+
*/
|
|
17
|
+
import { CommandResult } from './_shared.js';
|
|
18
|
+
export interface InitOptions {
|
|
19
|
+
cwd: string;
|
|
20
|
+
/** 必传:agent 名,单个或逗号分隔;空表示用户未提供。 */
|
|
21
|
+
agent?: string;
|
|
22
|
+
/** -g / --global 参数;true = 写到 ~/.<agent>/skills */
|
|
23
|
+
global?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function handleInit(options: InitOptions): Promise<CommandResult>;
|
|
26
|
+
/**
|
|
27
|
+
* 检查 raw 是否为「单一未知 agent 且无近似词候选」——
|
|
28
|
+
* CLI 层在此场景下会主动 prompt 用户是否写到通用目录 (.agents/skills/)。
|
|
29
|
+
*
|
|
30
|
+
* 返回 null 表示不属于此场景(合法 agent / 'all' / typo / 多 agent / 空);
|
|
31
|
+
* 返回字符串则是被识别出来的未知名(已 trim+lowercase)。
|
|
32
|
+
*/
|
|
33
|
+
export declare function detectUnknownAgentForPrompt(raw: string | undefined): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* 检查 raw 是否为「单一未知 agent 且有近似词候选」——
|
|
36
|
+
* CLI 层在此场景下会 prompt 用户是否直接使用建议的 agent。
|
|
37
|
+
*
|
|
38
|
+
* 返回 null 表示不属于此场景(合法 agent / 'all' / 多 agent / 空 / 无近似词);
|
|
39
|
+
* 返回 { input, suggestion } 则是识别出的 typo 和建议修正。
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectTypoAgentForPrompt(raw: string | undefined): {
|
|
42
|
+
input: string;
|
|
43
|
+
suggestion: string;
|
|
44
|
+
} | null;
|
|
45
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EACL,aAAa,EAOd,MAAM,cAAc,CAAC;AAatB,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAWD,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CA6G7E;AAuBD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAUlF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW9G"}
|