sentinelayer-cli 0.1.2 → 0.4.4
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 +998 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -54
- package/src/agents/jules/config/definition.js +209 -209
- package/src/agents/jules/config/system-prompt.js +175 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +377 -377
- package/src/agents/jules/loop.js +367 -367
- package/src/agents/jules/pulse.js +327 -319
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +308 -308
- package/src/agents/jules/tools/auth-audit.js +557 -222
- package/src/agents/jules/tools/dispatch.js +327 -327
- package/src/agents/jules/tools/file-edit.js +180 -180
- package/src/agents/jules/tools/file-read.js +100 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +168 -168
- package/src/agents/jules/tools/grep.js +228 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +161 -161
- package/src/agents/jules/tools/runtime-audit.js +503 -493
- package/src/agents/jules/tools/shell.js +383 -383
- package/src/agents/jules/tools/url-policy.js +100 -0
- package/src/ai/aidenid.js +972 -945
- package/src/ai/client.js +508 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +45 -11
- package/src/auth/http.js +270 -113
- package/src/auth/service.js +891 -848
- package/src/auth/session-store.js +359 -345
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1337
- package/src/commands/ai/provision-governance.js +1272 -1246
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +375 -366
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -404
- package/src/commands/omargate.js +15 -15
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +866 -788
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +510 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/legacy-cli.js +2592 -2435
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -106
- package/src/review/ai-review.js +669 -669
- package/src/review/local-review.js +1295 -1284
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -0
- package/src/scaffold/templates.js +150 -0
- package/src/scan/generator.js +418 -351
- package/src/scan/gh-secrets.js +107 -0
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/sync.js +107 -61
- package/src/ui/markdown.js +220 -220
package/src/commands/prompt.js
CHANGED
|
@@ -1,238 +1,238 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import fsp from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
import pc from "picocolors";
|
|
6
|
-
|
|
7
|
-
import { formatIngestResolutionNotice, resolveCodebaseIngest } from "../ingest/engine.js";
|
|
8
|
-
import {
|
|
9
|
-
defaultPromptFileName,
|
|
10
|
-
generateExecutionPrompt,
|
|
11
|
-
resolvePromptTarget,
|
|
12
|
-
SUPPORTED_PROMPT_TARGETS,
|
|
13
|
-
} from "../prompt/generator.js";
|
|
14
|
-
import { renderTerminalMarkdown } from "../ui/markdown.js";
|
|
15
|
-
|
|
16
|
-
function shouldEmitJson(options, command) {
|
|
17
|
-
const local = Boolean(options && options.json);
|
|
18
|
-
const globalFromCommand =
|
|
19
|
-
command && command.optsWithGlobals ? Boolean(command.optsWithGlobals().json) : false;
|
|
20
|
-
return local || globalFromCommand;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function resolveSpecPath(targetPath, explicitSpecFile) {
|
|
24
|
-
const explicit = String(explicitSpecFile || "").trim();
|
|
25
|
-
if (explicit) {
|
|
26
|
-
return path.resolve(targetPath, explicit);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const candidates = [path.join(targetPath, "SPEC.md"), path.join(targetPath, "docs", "spec.md")];
|
|
30
|
-
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
31
|
-
if (!found) {
|
|
32
|
-
throw new Error("No spec file found. Provide --spec-file or generate SPEC.md first.");
|
|
33
|
-
}
|
|
34
|
-
return found;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function buildPromptOutput({
|
|
38
|
-
targetPath,
|
|
39
|
-
specFile,
|
|
40
|
-
agent,
|
|
41
|
-
outputFile,
|
|
42
|
-
outputDir = "",
|
|
43
|
-
refresh = false,
|
|
44
|
-
}) {
|
|
45
|
-
const ingestResolution = await resolveCodebaseIngest({
|
|
46
|
-
rootPath: targetPath,
|
|
47
|
-
outputDir,
|
|
48
|
-
refresh,
|
|
49
|
-
});
|
|
50
|
-
const resolvedSpecPath = resolveSpecPath(targetPath, specFile);
|
|
51
|
-
const specMarkdown = await fsp.readFile(resolvedSpecPath, "utf-8");
|
|
52
|
-
const resolvedAgent = resolvePromptTarget(agent);
|
|
53
|
-
const promptMarkdown = generateExecutionPrompt({
|
|
54
|
-
specMarkdown,
|
|
55
|
-
target: resolvedAgent,
|
|
56
|
-
projectPath: targetPath,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const outputName = String(outputFile || "").trim() || defaultPromptFileName(resolvedAgent);
|
|
60
|
-
const outputPath = path.resolve(targetPath, outputName);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
agent: resolvedAgent,
|
|
64
|
-
specPath: resolvedSpecPath,
|
|
65
|
-
promptMarkdown,
|
|
66
|
-
outputPath,
|
|
67
|
-
ingestRefresh: {
|
|
68
|
-
outputPath: ingestResolution.outputPath,
|
|
69
|
-
refreshed: ingestResolution.refreshed,
|
|
70
|
-
stale: ingestResolution.stale,
|
|
71
|
-
reasons: ingestResolution.reasons,
|
|
72
|
-
refreshedBecause: ingestResolution.refreshedBecause,
|
|
73
|
-
lastCommitAt: ingestResolution.lastCommitAt,
|
|
74
|
-
contentHash: ingestResolution.fingerprint?.contentHash || "",
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function resolvePromptArtifactPath({ targetPath, promptFile, agent }) {
|
|
80
|
-
const explicit = String(promptFile || "").trim();
|
|
81
|
-
if (explicit) {
|
|
82
|
-
return path.resolve(targetPath, explicit);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const resolvedAgent = resolvePromptTarget(agent);
|
|
86
|
-
const defaultPath = path.resolve(targetPath, defaultPromptFileName(resolvedAgent));
|
|
87
|
-
if (!fs.existsSync(defaultPath)) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`No prompt artifact found at ${defaultPath}. Generate one with 'prompt generate' or pass --file.`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return defaultPath;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function registerPromptCommand(program) {
|
|
96
|
-
const prompt = program
|
|
97
|
-
.command("prompt")
|
|
98
|
-
.description("Generate agent execution prompts from SPEC content");
|
|
99
|
-
|
|
100
|
-
prompt
|
|
101
|
-
.command("generate")
|
|
102
|
-
.description("Generate prompt markdown file from spec")
|
|
103
|
-
.option("--path <path>", "Target workspace path", ".")
|
|
104
|
-
.option("--spec-file <path>", "Spec file path relative to --path")
|
|
105
|
-
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
106
|
-
.option("--output-file <path>", "Output prompt file path relative to --path")
|
|
107
|
-
.option("--output-dir <path>", "Optional output dir override for ingest cache")
|
|
108
|
-
.option("--refresh", "Refresh CODEBASE_INGEST before generating prompt")
|
|
109
|
-
.option("--json", "Emit machine-readable output")
|
|
110
|
-
.action(async (options, command) => {
|
|
111
|
-
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
112
|
-
const result = await buildPromptOutput({
|
|
113
|
-
targetPath,
|
|
114
|
-
specFile: options.specFile,
|
|
115
|
-
agent: options.agent,
|
|
116
|
-
outputFile: options.outputFile,
|
|
117
|
-
outputDir: options.outputDir,
|
|
118
|
-
refresh: Boolean(options.refresh),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
await fsp.mkdir(path.dirname(result.outputPath), { recursive: true });
|
|
122
|
-
await fsp.writeFile(result.outputPath, `${result.promptMarkdown.trimEnd()}\n`, "utf-8");
|
|
123
|
-
|
|
124
|
-
if (shouldEmitJson(options, command)) {
|
|
125
|
-
console.log(
|
|
126
|
-
JSON.stringify(
|
|
127
|
-
{
|
|
128
|
-
command: "prompt generate",
|
|
129
|
-
agent: result.agent,
|
|
130
|
-
specPath: result.specPath,
|
|
131
|
-
outputPath: result.outputPath,
|
|
132
|
-
ingestRefresh: result.ingestRefresh,
|
|
133
|
-
},
|
|
134
|
-
null,
|
|
135
|
-
2
|
|
136
|
-
)
|
|
137
|
-
);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
console.log(pc.bold("Prompt generated"));
|
|
142
|
-
console.log(pc.gray(`Agent: ${result.agent}`));
|
|
143
|
-
console.log(pc.gray(`Spec: ${result.specPath}`));
|
|
144
|
-
console.log(pc.gray(`Output: ${result.outputPath}`));
|
|
145
|
-
if (result.ingestRefresh?.stale || result.ingestRefresh?.refreshed) {
|
|
146
|
-
const color =
|
|
147
|
-
result.ingestRefresh?.stale && !result.ingestRefresh?.refreshed ? pc.yellow : pc.gray;
|
|
148
|
-
console.log(color(formatIngestResolutionNotice(result.ingestRefresh)));
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
prompt
|
|
153
|
-
.command("preview")
|
|
154
|
-
.description("Render generated prompt in terminal without writing file")
|
|
155
|
-
.option("--path <path>", "Target workspace path", ".")
|
|
156
|
-
.option("--spec-file <path>", "Spec file path relative to --path")
|
|
157
|
-
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
158
|
-
.option("--max-lines <n>", "Maximum lines to print (0 = unlimited)", "0")
|
|
159
|
-
.option("--output-dir <path>", "Optional output dir override for ingest cache")
|
|
160
|
-
.option("--refresh", "Refresh CODEBASE_INGEST before preview")
|
|
161
|
-
.option("--plain", "Disable terminal markdown styling")
|
|
162
|
-
.option("--json", "Emit machine-readable output")
|
|
163
|
-
.action(async (options, command) => {
|
|
164
|
-
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
165
|
-
const result = await buildPromptOutput({
|
|
166
|
-
targetPath,
|
|
167
|
-
specFile: options.specFile,
|
|
168
|
-
agent: options.agent,
|
|
169
|
-
outputDir: options.outputDir,
|
|
170
|
-
refresh: Boolean(options.refresh),
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const maxLines = Number.parseInt(String(options.maxLines || "0"), 10);
|
|
174
|
-
const lines = result.promptMarkdown.split(/\r?\n/);
|
|
175
|
-
const outputLines = Number.isFinite(maxLines) && maxLines > 0 ? lines.slice(0, maxLines) : lines;
|
|
176
|
-
|
|
177
|
-
if (shouldEmitJson(options, command)) {
|
|
178
|
-
console.log(
|
|
179
|
-
JSON.stringify(
|
|
180
|
-
{
|
|
181
|
-
command: "prompt preview",
|
|
182
|
-
agent: result.agent,
|
|
183
|
-
specPath: result.specPath,
|
|
184
|
-
lineCount: outputLines.length,
|
|
185
|
-
preview: outputLines.join("\n"),
|
|
186
|
-
ingestRefresh: result.ingestRefresh,
|
|
187
|
-
},
|
|
188
|
-
null,
|
|
189
|
-
2
|
|
190
|
-
)
|
|
191
|
-
);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (result.ingestRefresh?.stale || result.ingestRefresh?.refreshed) {
|
|
196
|
-
const color =
|
|
197
|
-
result.ingestRefresh?.stale && !result.ingestRefresh?.refreshed ? pc.yellow : pc.gray;
|
|
198
|
-
console.log(color(formatIngestResolutionNotice(result.ingestRefresh)));
|
|
199
|
-
}
|
|
200
|
-
console.log(renderTerminalMarkdown(outputLines.join("\n"), { plain: Boolean(options.plain) }));
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
prompt
|
|
204
|
-
.command("show")
|
|
205
|
-
.description("Render an existing prompt artifact in terminal markdown")
|
|
206
|
-
.option("--path <path>", "Target workspace path", ".")
|
|
207
|
-
.option("--file <path>", "Prompt file path relative to --path")
|
|
208
|
-
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
209
|
-
.option("--plain", "Disable terminal markdown styling")
|
|
210
|
-
.option("--json", "Emit machine-readable output")
|
|
211
|
-
.action(async (options, command) => {
|
|
212
|
-
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
213
|
-
const promptPath = resolvePromptArtifactPath({
|
|
214
|
-
targetPath,
|
|
215
|
-
promptFile: options.file,
|
|
216
|
-
agent: options.agent,
|
|
217
|
-
});
|
|
218
|
-
const markdown = await fsp.readFile(promptPath, "utf-8");
|
|
219
|
-
|
|
220
|
-
if (shouldEmitJson(options, command)) {
|
|
221
|
-
console.log(
|
|
222
|
-
JSON.stringify(
|
|
223
|
-
{
|
|
224
|
-
command: "prompt show",
|
|
225
|
-
promptPath,
|
|
226
|
-
lineCount: markdown.split(/\r?\n/).length,
|
|
227
|
-
preview: markdown,
|
|
228
|
-
},
|
|
229
|
-
null,
|
|
230
|
-
2
|
|
231
|
-
)
|
|
232
|
-
);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
console.log(renderTerminalMarkdown(markdown, { plain: Boolean(options.plain) }));
|
|
237
|
-
});
|
|
238
|
-
}
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import fsp from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
|
|
7
|
+
import { formatIngestResolutionNotice, resolveCodebaseIngest } from "../ingest/engine.js";
|
|
8
|
+
import {
|
|
9
|
+
defaultPromptFileName,
|
|
10
|
+
generateExecutionPrompt,
|
|
11
|
+
resolvePromptTarget,
|
|
12
|
+
SUPPORTED_PROMPT_TARGETS,
|
|
13
|
+
} from "../prompt/generator.js";
|
|
14
|
+
import { renderTerminalMarkdown } from "../ui/markdown.js";
|
|
15
|
+
|
|
16
|
+
function shouldEmitJson(options, command) {
|
|
17
|
+
const local = Boolean(options && options.json);
|
|
18
|
+
const globalFromCommand =
|
|
19
|
+
command && command.optsWithGlobals ? Boolean(command.optsWithGlobals().json) : false;
|
|
20
|
+
return local || globalFromCommand;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveSpecPath(targetPath, explicitSpecFile) {
|
|
24
|
+
const explicit = String(explicitSpecFile || "").trim();
|
|
25
|
+
if (explicit) {
|
|
26
|
+
return path.resolve(targetPath, explicit);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const candidates = [path.join(targetPath, "SPEC.md"), path.join(targetPath, "docs", "spec.md")];
|
|
30
|
+
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
31
|
+
if (!found) {
|
|
32
|
+
throw new Error("No spec file found. Provide --spec-file or generate SPEC.md first.");
|
|
33
|
+
}
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function buildPromptOutput({
|
|
38
|
+
targetPath,
|
|
39
|
+
specFile,
|
|
40
|
+
agent,
|
|
41
|
+
outputFile,
|
|
42
|
+
outputDir = "",
|
|
43
|
+
refresh = false,
|
|
44
|
+
}) {
|
|
45
|
+
const ingestResolution = await resolveCodebaseIngest({
|
|
46
|
+
rootPath: targetPath,
|
|
47
|
+
outputDir,
|
|
48
|
+
refresh,
|
|
49
|
+
});
|
|
50
|
+
const resolvedSpecPath = resolveSpecPath(targetPath, specFile);
|
|
51
|
+
const specMarkdown = await fsp.readFile(resolvedSpecPath, "utf-8");
|
|
52
|
+
const resolvedAgent = resolvePromptTarget(agent);
|
|
53
|
+
const promptMarkdown = generateExecutionPrompt({
|
|
54
|
+
specMarkdown,
|
|
55
|
+
target: resolvedAgent,
|
|
56
|
+
projectPath: targetPath,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const outputName = String(outputFile || "").trim() || defaultPromptFileName(resolvedAgent);
|
|
60
|
+
const outputPath = path.resolve(targetPath, outputName);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
agent: resolvedAgent,
|
|
64
|
+
specPath: resolvedSpecPath,
|
|
65
|
+
promptMarkdown,
|
|
66
|
+
outputPath,
|
|
67
|
+
ingestRefresh: {
|
|
68
|
+
outputPath: ingestResolution.outputPath,
|
|
69
|
+
refreshed: ingestResolution.refreshed,
|
|
70
|
+
stale: ingestResolution.stale,
|
|
71
|
+
reasons: ingestResolution.reasons,
|
|
72
|
+
refreshedBecause: ingestResolution.refreshedBecause,
|
|
73
|
+
lastCommitAt: ingestResolution.lastCommitAt,
|
|
74
|
+
contentHash: ingestResolution.fingerprint?.contentHash || "",
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolvePromptArtifactPath({ targetPath, promptFile, agent }) {
|
|
80
|
+
const explicit = String(promptFile || "").trim();
|
|
81
|
+
if (explicit) {
|
|
82
|
+
return path.resolve(targetPath, explicit);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const resolvedAgent = resolvePromptTarget(agent);
|
|
86
|
+
const defaultPath = path.resolve(targetPath, defaultPromptFileName(resolvedAgent));
|
|
87
|
+
if (!fs.existsSync(defaultPath)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`No prompt artifact found at ${defaultPath}. Generate one with 'prompt generate' or pass --file.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return defaultPath;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function registerPromptCommand(program) {
|
|
96
|
+
const prompt = program
|
|
97
|
+
.command("prompt")
|
|
98
|
+
.description("Generate agent execution prompts from SPEC content");
|
|
99
|
+
|
|
100
|
+
prompt
|
|
101
|
+
.command("generate")
|
|
102
|
+
.description("Generate prompt markdown file from spec")
|
|
103
|
+
.option("--path <path>", "Target workspace path", ".")
|
|
104
|
+
.option("--spec-file <path>", "Spec file path relative to --path")
|
|
105
|
+
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
106
|
+
.option("--output-file <path>", "Output prompt file path relative to --path")
|
|
107
|
+
.option("--output-dir <path>", "Optional output dir override for ingest cache")
|
|
108
|
+
.option("--refresh", "Refresh CODEBASE_INGEST before generating prompt")
|
|
109
|
+
.option("--json", "Emit machine-readable output")
|
|
110
|
+
.action(async (options, command) => {
|
|
111
|
+
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
112
|
+
const result = await buildPromptOutput({
|
|
113
|
+
targetPath,
|
|
114
|
+
specFile: options.specFile,
|
|
115
|
+
agent: options.agent,
|
|
116
|
+
outputFile: options.outputFile,
|
|
117
|
+
outputDir: options.outputDir,
|
|
118
|
+
refresh: Boolean(options.refresh),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await fsp.mkdir(path.dirname(result.outputPath), { recursive: true });
|
|
122
|
+
await fsp.writeFile(result.outputPath, `${result.promptMarkdown.trimEnd()}\n`, "utf-8");
|
|
123
|
+
|
|
124
|
+
if (shouldEmitJson(options, command)) {
|
|
125
|
+
console.log(
|
|
126
|
+
JSON.stringify(
|
|
127
|
+
{
|
|
128
|
+
command: "prompt generate",
|
|
129
|
+
agent: result.agent,
|
|
130
|
+
specPath: result.specPath,
|
|
131
|
+
outputPath: result.outputPath,
|
|
132
|
+
ingestRefresh: result.ingestRefresh,
|
|
133
|
+
},
|
|
134
|
+
null,
|
|
135
|
+
2
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(pc.bold("Prompt generated"));
|
|
142
|
+
console.log(pc.gray(`Agent: ${result.agent}`));
|
|
143
|
+
console.log(pc.gray(`Spec: ${result.specPath}`));
|
|
144
|
+
console.log(pc.gray(`Output: ${result.outputPath}`));
|
|
145
|
+
if (result.ingestRefresh?.stale || result.ingestRefresh?.refreshed) {
|
|
146
|
+
const color =
|
|
147
|
+
result.ingestRefresh?.stale && !result.ingestRefresh?.refreshed ? pc.yellow : pc.gray;
|
|
148
|
+
console.log(color(formatIngestResolutionNotice(result.ingestRefresh)));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
prompt
|
|
153
|
+
.command("preview")
|
|
154
|
+
.description("Render generated prompt in terminal without writing file")
|
|
155
|
+
.option("--path <path>", "Target workspace path", ".")
|
|
156
|
+
.option("--spec-file <path>", "Spec file path relative to --path")
|
|
157
|
+
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
158
|
+
.option("--max-lines <n>", "Maximum lines to print (0 = unlimited)", "0")
|
|
159
|
+
.option("--output-dir <path>", "Optional output dir override for ingest cache")
|
|
160
|
+
.option("--refresh", "Refresh CODEBASE_INGEST before preview")
|
|
161
|
+
.option("--plain", "Disable terminal markdown styling")
|
|
162
|
+
.option("--json", "Emit machine-readable output")
|
|
163
|
+
.action(async (options, command) => {
|
|
164
|
+
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
165
|
+
const result = await buildPromptOutput({
|
|
166
|
+
targetPath,
|
|
167
|
+
specFile: options.specFile,
|
|
168
|
+
agent: options.agent,
|
|
169
|
+
outputDir: options.outputDir,
|
|
170
|
+
refresh: Boolean(options.refresh),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const maxLines = Number.parseInt(String(options.maxLines || "0"), 10);
|
|
174
|
+
const lines = result.promptMarkdown.split(/\r?\n/);
|
|
175
|
+
const outputLines = Number.isFinite(maxLines) && maxLines > 0 ? lines.slice(0, maxLines) : lines;
|
|
176
|
+
|
|
177
|
+
if (shouldEmitJson(options, command)) {
|
|
178
|
+
console.log(
|
|
179
|
+
JSON.stringify(
|
|
180
|
+
{
|
|
181
|
+
command: "prompt preview",
|
|
182
|
+
agent: result.agent,
|
|
183
|
+
specPath: result.specPath,
|
|
184
|
+
lineCount: outputLines.length,
|
|
185
|
+
preview: outputLines.join("\n"),
|
|
186
|
+
ingestRefresh: result.ingestRefresh,
|
|
187
|
+
},
|
|
188
|
+
null,
|
|
189
|
+
2
|
|
190
|
+
)
|
|
191
|
+
);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (result.ingestRefresh?.stale || result.ingestRefresh?.refreshed) {
|
|
196
|
+
const color =
|
|
197
|
+
result.ingestRefresh?.stale && !result.ingestRefresh?.refreshed ? pc.yellow : pc.gray;
|
|
198
|
+
console.log(color(formatIngestResolutionNotice(result.ingestRefresh)));
|
|
199
|
+
}
|
|
200
|
+
console.log(renderTerminalMarkdown(outputLines.join("\n"), { plain: Boolean(options.plain) }));
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
prompt
|
|
204
|
+
.command("show")
|
|
205
|
+
.description("Render an existing prompt artifact in terminal markdown")
|
|
206
|
+
.option("--path <path>", "Target workspace path", ".")
|
|
207
|
+
.option("--file <path>", "Prompt file path relative to --path")
|
|
208
|
+
.option("--agent <target>", `Prompt target (${SUPPORTED_PROMPT_TARGETS.join("|")})`, "generic")
|
|
209
|
+
.option("--plain", "Disable terminal markdown styling")
|
|
210
|
+
.option("--json", "Emit machine-readable output")
|
|
211
|
+
.action(async (options, command) => {
|
|
212
|
+
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
213
|
+
const promptPath = resolvePromptArtifactPath({
|
|
214
|
+
targetPath,
|
|
215
|
+
promptFile: options.file,
|
|
216
|
+
agent: options.agent,
|
|
217
|
+
});
|
|
218
|
+
const markdown = await fsp.readFile(promptPath, "utf-8");
|
|
219
|
+
|
|
220
|
+
if (shouldEmitJson(options, command)) {
|
|
221
|
+
console.log(
|
|
222
|
+
JSON.stringify(
|
|
223
|
+
{
|
|
224
|
+
command: "prompt show",
|
|
225
|
+
promptPath,
|
|
226
|
+
lineCount: markdown.split(/\r?\n/).length,
|
|
227
|
+
preview: markdown,
|
|
228
|
+
},
|
|
229
|
+
null,
|
|
230
|
+
2
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log(renderTerminalMarkdown(markdown, { plain: Boolean(options.plain) }));
|
|
237
|
+
});
|
|
238
|
+
}
|