godpowers 3.13.0 → 3.13.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 +97 -0
- package/README.md +33 -25
- package/RELEASE.md +20 -21
- package/bin/install.js +1 -16
- package/hooks/pre-tool-use.sh +52 -40
- package/lib/README.md +12 -1
- package/lib/artifact-map.js +6 -0
- package/lib/cli-dispatch.js +29 -20
- package/lib/cli-log.js +24 -0
- package/lib/dashboard.js +1 -10
- package/lib/evidence.js +54 -13
- package/lib/gate.js +2 -2
- package/lib/have-nots-validator.js +5 -1
- package/lib/installer-args.js +140 -290
- package/lib/installer-core.js +1 -12
- package/lib/intent.js +9 -4
- package/lib/pillars.js +13 -0
- package/lib/planning-systems.js +2 -10
- package/lib/recipe-coverage-sync.js +4 -23
- package/lib/release-surface-sync.js +5 -32
- package/lib/repo-doc-sync.js +1 -16
- package/lib/repo-surface-sync.js +4 -55
- package/lib/requirements.js +6 -17
- package/lib/reverse-sync.js +9 -2
- package/lib/route-quality-sync.js +4 -33
- package/lib/source-sync.js +0 -4
- package/lib/state.js +19 -8
- package/lib/sync-check.js +56 -0
- package/lib/sync-fs.js +49 -0
- package/lib/text-util.js +19 -0
- package/lib/workflow-helper-groups.js +4 -0
- package/package.json +3 -3
- package/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +7 -0
- package/workflows/full-arc.yaml +18 -0
package/lib/evidence.js
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* Adaptations from the upstream Node engine (see .provenance.json):
|
|
11
11
|
* - Mythify's plan/step context becomes Godpowers' arc/substep context.
|
|
12
12
|
* - The .mythify/ state dir becomes .godpowers/ledger/.
|
|
13
|
-
* - The jsonl append
|
|
14
|
-
* torn
|
|
13
|
+
* - The jsonl append uses O_APPEND (fs.appendFileSync) so concurrent writers
|
|
14
|
+
* never clobber each other's records; a torn line from an interleaved large
|
|
15
|
+
* write is tolerated by the reader, which skips unparseable records.
|
|
15
16
|
*
|
|
16
17
|
* What this adds on top of the upstream engine (the Godpowers integration):
|
|
17
18
|
* 1. .godpowers/ledger/verifications.jsonl: append-only, Mythify-shape record,
|
|
@@ -61,6 +62,9 @@ const events = require('./events');
|
|
|
61
62
|
const TAIL_CHARS = 4000;
|
|
62
63
|
const DEFAULT_TIMEOUT_SECONDS = 300;
|
|
63
64
|
const DIAGNOSTICS_LIMIT = 1000;
|
|
65
|
+
// Cap on captured stdout/stderr per verify command. Exceeding it makes
|
|
66
|
+
// spawnSync raise ENOBUFS and truncate output, so the verdict is unreliable.
|
|
67
|
+
const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
64
68
|
|
|
65
69
|
// Substeps whose close gate requires an executed, verified:true record (the
|
|
66
70
|
// runtime/executable-gated tiers). Other substeps (planning, repo, observe,
|
|
@@ -161,22 +165,40 @@ function slugify(text) {
|
|
|
161
165
|
.slice(0, 40);
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
// SEC-003: mask obvious secret shapes before echoing a command into the
|
|
169
|
+
// human-readable LEDGER-LOG.md. The durable verifications.jsonl record keeps the
|
|
170
|
+
// exact command (it is the audit source of truth); this only protects the log
|
|
171
|
+
// echo. Output tails can still carry secrets, so SECURITY.md documents that
|
|
172
|
+
// .godpowers/ledger/ may capture sensitive output.
|
|
173
|
+
function redactSecrets(text) {
|
|
174
|
+
return String(text == null ? '' : text)
|
|
175
|
+
.replace(/\bgh[pousr]_[A-Za-z0-9]{16,}\b/g, 'gh*_***REDACTED***')
|
|
176
|
+
.replace(/\bsk-[A-Za-z0-9_-]{16,}\b/g, 'sk-***REDACTED***')
|
|
177
|
+
.replace(/\bAKIA[0-9A-Z]{16}\b/g, 'AKIA***REDACTED***')
|
|
178
|
+
.replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, 'xox*-***REDACTED***')
|
|
179
|
+
.replace(/(--?(?:token|password|passwd|secret|api[-_]?key)[=\s])\S+/gi, '$1***REDACTED***');
|
|
180
|
+
}
|
|
181
|
+
|
|
164
182
|
// ---------------------------------------------------------------------------
|
|
165
|
-
// Ledger append (
|
|
183
|
+
// Ledger append (O_APPEND) and tolerant read
|
|
166
184
|
// ---------------------------------------------------------------------------
|
|
167
185
|
|
|
168
186
|
function appendJsonlAtomic(file, record) {
|
|
169
187
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
// O_APPEND: each record is written at EOF in a single positioned write, so
|
|
189
|
+
// two concurrent writers never overwrite each other's record. The previous
|
|
190
|
+
// read-concat-rewrite was last-writer-wins (it lost records under concurrent
|
|
191
|
+
// invocation) and rewrote the whole file on every append (O(n) per record).
|
|
192
|
+
// A torn line from an interleaved oversized write is tolerated by readJsonl,
|
|
193
|
+
// which skips unparseable records. Mirrors the append in lib/events.js.
|
|
194
|
+
fs.appendFileSync(file, JSON.stringify(record) + '\n');
|
|
177
195
|
return file;
|
|
178
196
|
}
|
|
179
197
|
|
|
198
|
+
// Reads the whole ledger into memory. This is bounded and acceptable for a CLI:
|
|
199
|
+
// each record caps its stdout/stderr tails (TAIL_CHARS) so growth is slow, and
|
|
200
|
+
// every consumer reads it once per command, never in a loop. If a long-lived
|
|
201
|
+
// project's ledger ever grows large, add an opt-in prune/size cap here (PERF-002).
|
|
180
202
|
function readJsonl(file) {
|
|
181
203
|
let raw;
|
|
182
204
|
try {
|
|
@@ -255,18 +277,27 @@ function runCommand(command, timeoutSeconds) {
|
|
|
255
277
|
shell: true,
|
|
256
278
|
encoding: 'utf8',
|
|
257
279
|
timeout: Math.round(timeoutSeconds * 1000),
|
|
258
|
-
maxBuffer:
|
|
280
|
+
maxBuffer: MAX_OUTPUT_BYTES
|
|
259
281
|
});
|
|
260
282
|
const durationSeconds = Number(process.hrtime.bigint() - startedAt) / 1e9;
|
|
261
283
|
let stdoutTail = tail(run.stdout);
|
|
262
284
|
let stderrTail = tail(run.stderr);
|
|
263
285
|
const timedOut = Boolean(run.error && run.error.code === 'ETIMEDOUT');
|
|
286
|
+
const bufferOverflow = Boolean(run.error && run.error.code === 'ENOBUFS');
|
|
264
287
|
let exitCode;
|
|
265
288
|
let verified;
|
|
266
289
|
if (timedOut) {
|
|
267
290
|
exitCode = -1;
|
|
268
291
|
verified = false;
|
|
269
292
|
stderrTail = stderrTail + (stderrTail ? '\n' : '') + `(timed out after ${timeoutSeconds} seconds)`;
|
|
293
|
+
} else if (bufferOverflow) {
|
|
294
|
+
// Output exceeded the capture buffer: spawnSync truncated stdout/stderr, so
|
|
295
|
+
// we cannot trust the exit status. Surface this distinctly rather than
|
|
296
|
+
// folding it into a plain command failure.
|
|
297
|
+
exitCode = -1;
|
|
298
|
+
verified = false;
|
|
299
|
+
stderrTail = stderrTail + (stderrTail ? '\n' : '') +
|
|
300
|
+
`(output exceeded ${Math.round(MAX_OUTPUT_BYTES / (1024 * 1024))} MB buffer; output truncated and verdict unreliable)`;
|
|
270
301
|
} else if (typeof run.status === 'number') {
|
|
271
302
|
exitCode = run.status;
|
|
272
303
|
verified = exitCode === 0;
|
|
@@ -280,7 +311,7 @@ function runCommand(command, timeoutSeconds) {
|
|
|
280
311
|
: 'command did not produce an exit code';
|
|
281
312
|
stderrTail = stderrTail + (stderrTail ? '\n' : '') + `(${reason})`;
|
|
282
313
|
}
|
|
283
|
-
return { exitCode, verified, durationSeconds, stdoutTail, stderrTail, timedOut };
|
|
314
|
+
return { exitCode, verified, durationSeconds, stdoutTail, stderrTail, timedOut, bufferOverflow };
|
|
284
315
|
}
|
|
285
316
|
|
|
286
317
|
// ---------------------------------------------------------------------------
|
|
@@ -449,7 +480,7 @@ function verify(command, opts = {}) {
|
|
|
449
480
|
appendRecord(projectRoot, record);
|
|
450
481
|
appendLog(
|
|
451
482
|
projectRoot,
|
|
452
|
-
`verify ${record.verified ? 'PASS' : 'FAIL'} substep=${context.substep || '-'} exit=${record.exit_code} cmd=\`${command}\``
|
|
483
|
+
`verify ${record.verified ? 'PASS' : 'FAIL'} substep=${context.substep || '-'} exit=${record.exit_code} cmd=\`${redactSecrets(command)}\``
|
|
453
484
|
);
|
|
454
485
|
|
|
455
486
|
const rollup = rollUp(projectRoot, opts.substep, record);
|
|
@@ -818,6 +849,15 @@ function outcomeCheck(name, opts = {}) {
|
|
|
818
849
|
if (goal.status !== 'active') return { ran: false, reason: `outcome-${goal.status}`, goal };
|
|
819
850
|
if (!goal.verifier) return { ran: false, reason: 'no-verifier', goal };
|
|
820
851
|
|
|
852
|
+
// SEC-002: the verifier is a shell command read from goal.json on disk, not
|
|
853
|
+
// from a live flag, so `outcome check` in a cloned untrusted repo would
|
|
854
|
+
// otherwise execute a planted command silently. Surface what is about to run
|
|
855
|
+
// and where it came from before executing. The notice is informational; the
|
|
856
|
+
// CLI passes one that prints to stderr.
|
|
857
|
+
if (typeof opts.notice === 'function') {
|
|
858
|
+
opts.notice({ verifier: goal.verifier, source: outcomeGoalPath(projectRoot, slug) });
|
|
859
|
+
}
|
|
860
|
+
|
|
821
861
|
const result = verify(goal.verifier, {
|
|
822
862
|
substep: goal.substep || undefined,
|
|
823
863
|
claim: goal.title,
|
|
@@ -901,6 +941,7 @@ module.exports = {
|
|
|
901
941
|
DEFAULT_TIMEOUT_SECONDS,
|
|
902
942
|
// Internals exposed for tests and the re-sync script.
|
|
903
943
|
_runCommand: runCommand,
|
|
944
|
+
_redactSecrets: redactSecrets,
|
|
904
945
|
_toStateCommand: toStateCommand,
|
|
905
946
|
_substepContext: substepContext,
|
|
906
947
|
_rollUp: rollUp,
|
package/lib/gate.js
CHANGED
|
@@ -215,7 +215,7 @@ function checkStateStepEvidence(projectRoot, tier, result) {
|
|
|
215
215
|
const stepRef = artifactMap.stateStepForTier(tier);
|
|
216
216
|
if (!stepRef) return null;
|
|
217
217
|
|
|
218
|
-
const relPath =
|
|
218
|
+
const relPath = stateStore.STATE_FILE;
|
|
219
219
|
const currentState = stateStore.read(projectRoot);
|
|
220
220
|
if (!currentState) {
|
|
221
221
|
const finding = makeFinding(
|
|
@@ -275,7 +275,7 @@ function checkStateStepEvidence(projectRoot, tier, result) {
|
|
|
275
275
|
// tier-prefixed so the build tier keeps its existing `build-verification-*`
|
|
276
276
|
// contract while harden gains `harden-verification-*`.
|
|
277
277
|
function checkExecutedEvidence(result, step, tier) {
|
|
278
|
-
const relPath =
|
|
278
|
+
const relPath = stateStore.STATE_FILE;
|
|
279
279
|
if (!step) return;
|
|
280
280
|
const label = tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
281
281
|
const failedCommands = commandsWithStatus(step, 'fail');
|
|
@@ -53,9 +53,13 @@ const LABEL_TAGS = ['DECISION', 'HYPOTHESIS', 'OPEN QUESTION', 'OPEN-QUESTION'];
|
|
|
53
53
|
function findPositions(content, regex) {
|
|
54
54
|
const positions = [];
|
|
55
55
|
const lines = content.split('\n');
|
|
56
|
+
// Compile once and reuse across lines (PERF-001); reset lastIndex per line so
|
|
57
|
+
// matching is identical to a fresh per-line regex.
|
|
58
|
+
const flags = regex.flags.includes('g') ? regex.flags : regex.flags + 'g';
|
|
59
|
+
const localRegex = new RegExp(regex.source, flags);
|
|
56
60
|
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
localRegex.lastIndex = 0;
|
|
57
62
|
let match;
|
|
58
|
-
const localRegex = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g');
|
|
59
63
|
while ((match = localRegex.exec(lines[i])) !== null) {
|
|
60
64
|
positions.push({ line: i + 1, column: match.index + 1, matched: match[0] });
|
|
61
65
|
}
|
package/lib/installer-args.js
CHANGED
|
@@ -25,9 +25,74 @@ const COMMANDS = new Set([
|
|
|
25
25
|
'import-ledger'
|
|
26
26
|
]);
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// Per-command positional slots, filled in order into the first empty slot.
|
|
29
|
+
const POSITIONALS = {
|
|
30
|
+
state: ['stateAction'],
|
|
31
|
+
verify: ['verifyCommand'],
|
|
32
|
+
route: ['routePrompt'],
|
|
33
|
+
memory: ['memoryAction', 'memoryKey', 'memoryValue'],
|
|
34
|
+
lesson: ['lessonAction', 'lessonText'],
|
|
35
|
+
outcome: ['outcomeAction', 'outcomeSlug']
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Boolean flags (and their aliases) -> opts field set to true.
|
|
39
|
+
const BOOL_FLAGS = {
|
|
40
|
+
'--json': 'json',
|
|
41
|
+
'--brief': 'brief',
|
|
42
|
+
'--full': 'full',
|
|
43
|
+
'--apply': 'apply',
|
|
44
|
+
'--dry-run': 'dryRun',
|
|
45
|
+
'--attest': 'attest',
|
|
46
|
+
'--peek': 'peek',
|
|
47
|
+
'--all': 'all',
|
|
48
|
+
'-g': 'global',
|
|
49
|
+
'--global': 'global',
|
|
50
|
+
'-l': 'local',
|
|
51
|
+
'--local': 'local',
|
|
52
|
+
'-h': 'help',
|
|
53
|
+
'--help': 'help',
|
|
54
|
+
'-u': 'uninstall',
|
|
55
|
+
'--uninstall': 'uninstall'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Value flags -> opts field. Each accepts both "--flag value" and "--flag=value".
|
|
59
|
+
const VALUE_FLAGS = {
|
|
60
|
+
'--tier': 'tier',
|
|
61
|
+
'--step': 'step',
|
|
62
|
+
'--status': 'status',
|
|
63
|
+
'--substep': 'substep',
|
|
64
|
+
'--claim': 'claim',
|
|
65
|
+
'--timeout': 'timeout',
|
|
66
|
+
'--evidence': 'evidence',
|
|
67
|
+
'--since': 'since',
|
|
68
|
+
'--action': 'reflectAction',
|
|
69
|
+
'--outcome': 'outcome',
|
|
70
|
+
'--observation': 'observation',
|
|
71
|
+
'--root-cause': 'rootCause',
|
|
72
|
+
'--next': 'nextAction',
|
|
73
|
+
'--lesson': 'lesson',
|
|
74
|
+
'--category': 'category',
|
|
75
|
+
'--tags': 'tags',
|
|
76
|
+
'--scope': 'scope',
|
|
77
|
+
'--goal': 'outcomeGoal',
|
|
78
|
+
'--verify': 'outcomeVerify',
|
|
79
|
+
'--budget': 'budget',
|
|
80
|
+
'--reason': 'reason',
|
|
81
|
+
'--from': 'importFrom',
|
|
82
|
+
'--profile': 'profile',
|
|
83
|
+
'--project': 'project',
|
|
84
|
+
'--name': 'extensionName',
|
|
85
|
+
'--output': 'extensionOutput',
|
|
86
|
+
'--skill': 'extensionSkill',
|
|
87
|
+
'--agent': 'extensionAgent',
|
|
88
|
+
'--workflow': 'extensionWorkflow'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Value flags whose value is resolved to an absolute path.
|
|
92
|
+
const PATH_FLAGS = new Set(['--project', '--output']);
|
|
93
|
+
|
|
94
|
+
function defaultOpts(cwd) {
|
|
95
|
+
return {
|
|
31
96
|
command: null,
|
|
32
97
|
project: cwd,
|
|
33
98
|
json: false,
|
|
@@ -80,301 +145,86 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
80
145
|
all: false,
|
|
81
146
|
help: false,
|
|
82
147
|
uninstall: false,
|
|
83
|
-
profile: 'core'
|
|
148
|
+
profile: 'core'
|
|
84
149
|
};
|
|
150
|
+
}
|
|
85
151
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (opts.command === 'route' && opts.routePrompt === null && !arg.startsWith('-')) {
|
|
101
|
-
opts.routePrompt = arg;
|
|
102
|
-
continue;
|
|
152
|
+
function setValue(opts, flag, value) {
|
|
153
|
+
opts[VALUE_FLAGS[flag]] = PATH_FLAGS.has(flag) ? path.resolve(value) : value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fill the first empty positional slot for the active command. Returns true when
|
|
157
|
+
// the arg was consumed as a positional.
|
|
158
|
+
function consumePositional(opts, arg) {
|
|
159
|
+
if (arg.startsWith('-')) return false;
|
|
160
|
+
const slots = POSITIONALS[opts.command];
|
|
161
|
+
if (!slots) return false;
|
|
162
|
+
for (const slot of slots) {
|
|
163
|
+
if (opts[slot] === null) {
|
|
164
|
+
opts[slot] = arg;
|
|
165
|
+
return true;
|
|
103
166
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Handle one flag at args[i]. Returns the index to continue from (advanced past
|
|
172
|
+
// a consumed value for space-form flags).
|
|
173
|
+
function consumeFlag(opts, args, i) {
|
|
174
|
+
const arg = args[i];
|
|
175
|
+
|
|
176
|
+
if (arg in BOOL_FLAGS) {
|
|
177
|
+
opts[BOOL_FLAGS[arg]] = true;
|
|
178
|
+
return i;
|
|
179
|
+
}
|
|
180
|
+
if (arg === '--minimal') {
|
|
181
|
+
opts.profile = 'core';
|
|
182
|
+
return i;
|
|
183
|
+
}
|
|
184
|
+
if (arg === '--runtime') {
|
|
185
|
+
if (args[i + 1]) {
|
|
186
|
+
opts.runtimes.push(args[i + 1]);
|
|
187
|
+
return i + 1;
|
|
108
188
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
189
|
+
return i;
|
|
190
|
+
}
|
|
191
|
+
if (arg.startsWith('--runtime=')) {
|
|
192
|
+
opts.runtimes.push(arg.slice('--runtime='.length));
|
|
193
|
+
return i;
|
|
194
|
+
}
|
|
195
|
+
if (arg in VALUE_FLAGS) {
|
|
196
|
+
if (args[i + 1]) {
|
|
197
|
+
setValue(opts, arg, args[i + 1]);
|
|
198
|
+
return i + 1;
|
|
112
199
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
200
|
+
return i;
|
|
201
|
+
}
|
|
202
|
+
const eq = arg.indexOf('=');
|
|
203
|
+
if (arg.startsWith('--') && eq > 0) {
|
|
204
|
+
const name = arg.slice(0, eq);
|
|
205
|
+
if (name in VALUE_FLAGS) {
|
|
206
|
+
setValue(opts, name, arg.slice(eq + 1));
|
|
207
|
+
return i;
|
|
116
208
|
}
|
|
209
|
+
}
|
|
210
|
+
if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
|
211
|
+
opts.runtimes.push(arg.slice(2));
|
|
212
|
+
}
|
|
213
|
+
return i;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function parseArgs(argv, cwd = process.cwd()) {
|
|
217
|
+
const args = argv.slice(2);
|
|
218
|
+
const opts = defaultOpts(cwd);
|
|
117
219
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
opts.brief = true;
|
|
124
|
-
break;
|
|
125
|
-
case '--full':
|
|
126
|
-
opts.full = true;
|
|
127
|
-
break;
|
|
128
|
-
case '--apply':
|
|
129
|
-
opts.apply = true;
|
|
130
|
-
break;
|
|
131
|
-
case '--dry-run':
|
|
132
|
-
opts.dryRun = true;
|
|
133
|
-
break;
|
|
134
|
-
case '--runtime':
|
|
135
|
-
if (args[i + 1]) {
|
|
136
|
-
opts.runtimes.push(args[i + 1]);
|
|
137
|
-
i++;
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
case '--tier':
|
|
141
|
-
if (args[i + 1]) {
|
|
142
|
-
opts.tier = args[i + 1];
|
|
143
|
-
i++;
|
|
144
|
-
}
|
|
145
|
-
break;
|
|
146
|
-
case '--step':
|
|
147
|
-
if (args[i + 1]) {
|
|
148
|
-
opts.step = args[i + 1];
|
|
149
|
-
i++;
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
case '--status':
|
|
153
|
-
if (args[i + 1]) {
|
|
154
|
-
opts.status = args[i + 1];
|
|
155
|
-
i++;
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
case '--substep':
|
|
159
|
-
if (args[i + 1]) {
|
|
160
|
-
opts.substep = args[i + 1];
|
|
161
|
-
i++;
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
case '--claim':
|
|
165
|
-
if (args[i + 1]) {
|
|
166
|
-
opts.claim = args[i + 1];
|
|
167
|
-
i++;
|
|
168
|
-
}
|
|
169
|
-
break;
|
|
170
|
-
case '--timeout':
|
|
171
|
-
if (args[i + 1]) {
|
|
172
|
-
opts.timeout = args[i + 1];
|
|
173
|
-
i++;
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
case '--evidence':
|
|
177
|
-
if (args[i + 1]) {
|
|
178
|
-
opts.evidence = args[i + 1];
|
|
179
|
-
i++;
|
|
180
|
-
}
|
|
181
|
-
break;
|
|
182
|
-
case '--attest':
|
|
183
|
-
opts.attest = true;
|
|
184
|
-
break;
|
|
185
|
-
case '--peek':
|
|
186
|
-
opts.peek = true;
|
|
187
|
-
break;
|
|
188
|
-
case '--since':
|
|
189
|
-
if (args[i + 1]) {
|
|
190
|
-
opts.since = args[i + 1];
|
|
191
|
-
i++;
|
|
192
|
-
}
|
|
193
|
-
break;
|
|
194
|
-
case '--action':
|
|
195
|
-
if (args[i + 1]) {
|
|
196
|
-
opts.reflectAction = args[i + 1];
|
|
197
|
-
i++;
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
case '--outcome':
|
|
201
|
-
if (args[i + 1]) {
|
|
202
|
-
opts.outcome = args[i + 1];
|
|
203
|
-
i++;
|
|
204
|
-
}
|
|
205
|
-
break;
|
|
206
|
-
case '--observation':
|
|
207
|
-
if (args[i + 1]) {
|
|
208
|
-
opts.observation = args[i + 1];
|
|
209
|
-
i++;
|
|
210
|
-
}
|
|
211
|
-
break;
|
|
212
|
-
case '--root-cause':
|
|
213
|
-
if (args[i + 1]) {
|
|
214
|
-
opts.rootCause = args[i + 1];
|
|
215
|
-
i++;
|
|
216
|
-
}
|
|
217
|
-
break;
|
|
218
|
-
case '--next':
|
|
219
|
-
if (args[i + 1]) {
|
|
220
|
-
opts.nextAction = args[i + 1];
|
|
221
|
-
i++;
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
case '--lesson':
|
|
225
|
-
if (args[i + 1]) {
|
|
226
|
-
opts.lesson = args[i + 1];
|
|
227
|
-
i++;
|
|
228
|
-
}
|
|
229
|
-
break;
|
|
230
|
-
case '--category':
|
|
231
|
-
if (args[i + 1]) {
|
|
232
|
-
opts.category = args[i + 1];
|
|
233
|
-
i++;
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
236
|
-
case '--tags':
|
|
237
|
-
if (args[i + 1]) {
|
|
238
|
-
opts.tags = args[i + 1];
|
|
239
|
-
i++;
|
|
240
|
-
}
|
|
241
|
-
break;
|
|
242
|
-
case '--scope':
|
|
243
|
-
if (args[i + 1]) {
|
|
244
|
-
opts.scope = args[i + 1];
|
|
245
|
-
i++;
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
case '--goal':
|
|
249
|
-
if (args[i + 1]) {
|
|
250
|
-
opts.outcomeGoal = args[i + 1];
|
|
251
|
-
i++;
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
case '--verify':
|
|
255
|
-
if (args[i + 1]) {
|
|
256
|
-
opts.outcomeVerify = args[i + 1];
|
|
257
|
-
i++;
|
|
258
|
-
}
|
|
259
|
-
break;
|
|
260
|
-
case '--budget':
|
|
261
|
-
if (args[i + 1]) {
|
|
262
|
-
opts.budget = args[i + 1];
|
|
263
|
-
i++;
|
|
264
|
-
}
|
|
265
|
-
break;
|
|
266
|
-
case '--reason':
|
|
267
|
-
if (args[i + 1]) {
|
|
268
|
-
opts.reason = args[i + 1];
|
|
269
|
-
i++;
|
|
270
|
-
}
|
|
271
|
-
break;
|
|
272
|
-
case '--from':
|
|
273
|
-
if (args[i + 1]) {
|
|
274
|
-
opts.importFrom = args[i + 1];
|
|
275
|
-
i++;
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
case '--project':
|
|
279
|
-
if (args[i + 1]) {
|
|
280
|
-
opts.project = path.resolve(args[i + 1]);
|
|
281
|
-
i++;
|
|
282
|
-
}
|
|
283
|
-
break;
|
|
284
|
-
case '-g':
|
|
285
|
-
case '--global':
|
|
286
|
-
opts.global = true;
|
|
287
|
-
break;
|
|
288
|
-
case '-l':
|
|
289
|
-
case '--local':
|
|
290
|
-
opts.local = true;
|
|
291
|
-
break;
|
|
292
|
-
case '--all':
|
|
293
|
-
opts.all = true;
|
|
294
|
-
break;
|
|
295
|
-
case '--minimal':
|
|
296
|
-
opts.profile = 'core';
|
|
297
|
-
break;
|
|
298
|
-
case '--profile':
|
|
299
|
-
if (args[i + 1]) {
|
|
300
|
-
opts.profile = args[i + 1];
|
|
301
|
-
i++;
|
|
302
|
-
}
|
|
303
|
-
break;
|
|
304
|
-
case '-h':
|
|
305
|
-
case '--help':
|
|
306
|
-
opts.help = true;
|
|
307
|
-
break;
|
|
308
|
-
case '-u':
|
|
309
|
-
case '--uninstall':
|
|
310
|
-
opts.uninstall = true;
|
|
311
|
-
break;
|
|
312
|
-
default:
|
|
313
|
-
if (arg.startsWith('--project=')) {
|
|
314
|
-
opts.project = path.resolve(arg.slice('--project='.length));
|
|
315
|
-
} else if (arg.startsWith('--name=')) {
|
|
316
|
-
opts.extensionName = arg.slice('--name='.length);
|
|
317
|
-
} else if (arg.startsWith('--output=')) {
|
|
318
|
-
opts.extensionOutput = path.resolve(arg.slice('--output='.length));
|
|
319
|
-
} else if (arg.startsWith('--skill=')) {
|
|
320
|
-
opts.extensionSkill = arg.slice('--skill='.length);
|
|
321
|
-
} else if (arg.startsWith('--agent=')) {
|
|
322
|
-
opts.extensionAgent = arg.slice('--agent='.length);
|
|
323
|
-
} else if (arg.startsWith('--workflow=')) {
|
|
324
|
-
opts.extensionWorkflow = arg.slice('--workflow='.length);
|
|
325
|
-
} else if (arg.startsWith('--runtime=')) {
|
|
326
|
-
opts.runtimes.push(arg.slice('--runtime='.length));
|
|
327
|
-
} else if (arg.startsWith('--tier=')) {
|
|
328
|
-
opts.tier = arg.slice('--tier='.length);
|
|
329
|
-
} else if (arg.startsWith('--step=')) {
|
|
330
|
-
opts.step = arg.slice('--step='.length);
|
|
331
|
-
} else if (arg.startsWith('--status=')) {
|
|
332
|
-
opts.status = arg.slice('--status='.length);
|
|
333
|
-
} else if (arg.startsWith('--substep=')) {
|
|
334
|
-
opts.substep = arg.slice('--substep='.length);
|
|
335
|
-
} else if (arg.startsWith('--claim=')) {
|
|
336
|
-
opts.claim = arg.slice('--claim='.length);
|
|
337
|
-
} else if (arg.startsWith('--timeout=')) {
|
|
338
|
-
opts.timeout = arg.slice('--timeout='.length);
|
|
339
|
-
} else if (arg.startsWith('--evidence=')) {
|
|
340
|
-
opts.evidence = arg.slice('--evidence='.length);
|
|
341
|
-
} else if (arg.startsWith('--since=')) {
|
|
342
|
-
opts.since = arg.slice('--since='.length);
|
|
343
|
-
} else if (arg.startsWith('--action=')) {
|
|
344
|
-
opts.reflectAction = arg.slice('--action='.length);
|
|
345
|
-
} else if (arg.startsWith('--outcome=')) {
|
|
346
|
-
opts.outcome = arg.slice('--outcome='.length);
|
|
347
|
-
} else if (arg.startsWith('--observation=')) {
|
|
348
|
-
opts.observation = arg.slice('--observation='.length);
|
|
349
|
-
} else if (arg.startsWith('--root-cause=')) {
|
|
350
|
-
opts.rootCause = arg.slice('--root-cause='.length);
|
|
351
|
-
} else if (arg.startsWith('--next=')) {
|
|
352
|
-
opts.nextAction = arg.slice('--next='.length);
|
|
353
|
-
} else if (arg.startsWith('--lesson=')) {
|
|
354
|
-
opts.lesson = arg.slice('--lesson='.length);
|
|
355
|
-
} else if (arg.startsWith('--category=')) {
|
|
356
|
-
opts.category = arg.slice('--category='.length);
|
|
357
|
-
} else if (arg.startsWith('--tags=')) {
|
|
358
|
-
opts.tags = arg.slice('--tags='.length);
|
|
359
|
-
} else if (arg.startsWith('--scope=')) {
|
|
360
|
-
opts.scope = arg.slice('--scope='.length);
|
|
361
|
-
} else if (arg.startsWith('--goal=')) {
|
|
362
|
-
opts.outcomeGoal = arg.slice('--goal='.length);
|
|
363
|
-
} else if (arg.startsWith('--verify=')) {
|
|
364
|
-
opts.outcomeVerify = arg.slice('--verify='.length);
|
|
365
|
-
} else if (arg.startsWith('--budget=')) {
|
|
366
|
-
opts.budget = arg.slice('--budget='.length);
|
|
367
|
-
} else if (arg.startsWith('--reason=')) {
|
|
368
|
-
opts.reason = arg.slice('--reason='.length);
|
|
369
|
-
} else if (arg.startsWith('--from=')) {
|
|
370
|
-
opts.importFrom = arg.slice('--from='.length);
|
|
371
|
-
} else if (arg.startsWith('--profile=')) {
|
|
372
|
-
opts.profile = arg.slice('--profile='.length);
|
|
373
|
-
} else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
|
|
374
|
-
opts.runtimes.push(arg.slice(2));
|
|
375
|
-
}
|
|
376
|
-
break;
|
|
220
|
+
for (let i = 0; i < args.length; i++) {
|
|
221
|
+
const arg = args[i];
|
|
222
|
+
if (COMMANDS.has(arg)) {
|
|
223
|
+
opts.command = arg;
|
|
224
|
+
continue;
|
|
377
225
|
}
|
|
226
|
+
if (consumePositional(opts, arg)) continue;
|
|
227
|
+
i = consumeFlag(opts, args, i);
|
|
378
228
|
}
|
|
379
229
|
|
|
380
230
|
return opts;
|
package/lib/installer-core.js
CHANGED
|
@@ -6,6 +6,7 @@ const { resolveRuntime } = require('./installer-runtimes');
|
|
|
6
6
|
const { selectedSkillNames, normalizeProfiles } = require('./install-profiles');
|
|
7
7
|
const identity = require('./package-identity');
|
|
8
8
|
const frontmatter = require('./frontmatter');
|
|
9
|
+
const { log, success, error } = require('./cli-log');
|
|
9
10
|
|
|
10
11
|
const VERSION = identity.PACKAGE_VERSION;
|
|
11
12
|
|
|
@@ -21,18 +22,6 @@ const VERSION = identity.PACKAGE_VERSION;
|
|
|
21
22
|
* @property {number} agents Number of specialist agent files.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
|
-
function log(msg) {
|
|
25
|
-
console.log(` ${msg}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function success(msg) {
|
|
29
|
-
console.log(` \x1b[32m+\x1b[0m ${msg}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function error(msg) {
|
|
33
|
-
console.error(` \x1b[31mx\x1b[0m ${msg}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
25
|
function installSkillFile(srcFile, skillsDest, runtimeKey, targetName = null) {
|
|
37
26
|
const baseName = targetName || path.basename(srcFile, '.md');
|
|
38
27
|
if (runtimeKey === 'codex') {
|
package/lib/intent.js
CHANGED
|
@@ -332,15 +332,20 @@ function splitInlineArray(text) {
|
|
|
332
332
|
return parts;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
// Far beyond any legitimate config nesting; caps recursion so a hostile,
|
|
336
|
+
// thousands-deep YAML file cannot overflow the stack (SEC-002).
|
|
337
|
+
const MAX_CLEAN_DEPTH = 200;
|
|
338
|
+
|
|
339
|
+
function cleanArrays(obj, depth = 0) {
|
|
340
|
+
if (depth > MAX_CLEAN_DEPTH) return obj;
|
|
341
|
+
if (Array.isArray(obj)) return obj.map((v) => cleanArrays(v, depth + 1));
|
|
337
342
|
if (obj && typeof obj === 'object') {
|
|
338
343
|
// Detect array container (legacy or new)
|
|
339
|
-
if (obj.__items__) return obj.__items__.map(cleanArrays);
|
|
344
|
+
if (obj.__items__) return obj.__items__.map((v) => cleanArrays(v, depth + 1));
|
|
340
345
|
if (obj.__pending_array__) return obj.__pending_array__;
|
|
341
346
|
const cleaned = {};
|
|
342
347
|
for (const [k, v] of Object.entries(obj)) {
|
|
343
|
-
cleaned[k] = cleanArrays(v);
|
|
348
|
+
cleaned[k] = cleanArrays(v, depth + 1);
|
|
344
349
|
}
|
|
345
350
|
return cleaned;
|
|
346
351
|
}
|