devflow-kit 3.3.0 → 3.5.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/CHANGELOG.md +47 -0
- package/README.md +8 -4
- package/dist/agents/code-edit.md +133 -0
- package/dist/agents/code-fix.md +165 -0
- package/dist/agents/code.md +187 -0
- package/{src/assets → dist}/agents/design.md +1 -2
- package/{src/assets → dist}/agents/diagnose.md +1 -2
- package/dist/agents/git.md +33 -722
- package/{src/assets → dist}/agents/knowledge.md +12 -5
- package/{src/assets → dist}/agents/research.md +8 -2
- package/{src/assets → dist}/agents/review.md +8 -11
- package/{src/assets → dist}/agents/scrutinize.md +4 -1
- package/dist/agents/skim.md +152 -0
- package/{src/assets → dist}/agents/triage.md +9 -2
- package/dist/cli/commands/behavior-prompts.js +77 -0
- package/dist/cli/commands/behavior.js +123 -0
- package/dist/cli/commands/flags.js +8 -2
- package/dist/cli/commands/init-seed.js +7 -3
- package/dist/cli/commands/init.js +134 -5
- package/dist/cli/commands/learning.js +38 -3
- package/dist/cli/commands/rules.js +16 -0
- package/dist/cli/commands/tracker.js +112 -15
- package/dist/cli/commands/uninstall.js +42 -1
- package/dist/cli.js +2 -0
- package/dist/commands/bug-analysis.md +35 -13
- package/dist/commands/code-review.md +155 -69
- package/dist/commands/debug.md +39 -16
- package/dist/commands/dynamic-build.md +61 -72
- package/dist/commands/dynamic-plan.md +39 -25
- package/dist/commands/dynamic-profile.md +28 -14
- package/dist/commands/dynamic-tickets.md +38 -22
- package/dist/commands/explore.md +18 -16
- package/dist/commands/implement.md +121 -150
- package/dist/commands/plan.md +51 -31
- package/dist/commands/release.md +72 -6
- package/dist/commands/research.md +49 -20
- package/dist/commands/resolve.md +54 -46
- package/dist/commands/self-review.md +38 -25
- package/dist/core/agent-frontmatter.js +101 -0
- package/dist/core/agent-models.js +43 -0
- package/dist/core/assets.js +55 -10
- package/dist/core/claude-md-audit.js +190 -0
- package/dist/core/compliance-compose.js +6 -4
- package/dist/core/feature-switch.js +41 -1
- package/dist/core/flags.js +139 -0
- package/dist/core/fs-atomic.js +8 -3
- package/dist/core/learning-variants.js +213 -0
- package/dist/core/manifest.js +73 -3
- package/dist/core/mds-variants.js +98 -19
- package/dist/core/plugins.js +111 -72
- package/dist/core/project-paths.js +0 -4
- package/dist/core/proxy-log.js +1 -1
- package/dist/core/proxy-state.js +1 -1
- package/dist/core/tracker.js +120 -2
- package/dist/learning-off/agents/code-edit.md +129 -0
- package/dist/learning-off/agents/code-fix.md +161 -0
- package/dist/learning-off/agents/code.md +183 -0
- package/dist/learning-off/agents/design.md +118 -0
- package/dist/learning-off/agents/diagnose.md +210 -0
- package/dist/learning-off/agents/knowledge.md +96 -0
- package/dist/learning-off/agents/research.md +155 -0
- package/dist/learning-off/agents/review.md +224 -0
- package/dist/learning-off/agents/scrutinize.md +120 -0
- package/{src/assets → dist/learning-off}/agents/skim.md +5 -8
- package/dist/learning-off/agents/triage.md +170 -0
- package/dist/learning-off/commands/bug-analysis.md +420 -0
- package/dist/learning-off/commands/code-review.md +530 -0
- package/dist/learning-off/commands/debug.md +314 -0
- package/dist/learning-off/commands/dynamic-build.md +1245 -0
- package/dist/learning-off/commands/dynamic-plan.md +426 -0
- package/dist/learning-off/commands/dynamic-profile.md +214 -0
- package/dist/learning-off/commands/dynamic-tickets.md +634 -0
- package/dist/learning-off/commands/explore.md +210 -0
- package/dist/learning-off/commands/implement.md +774 -0
- package/dist/learning-off/commands/plan.md +671 -0
- package/dist/learning-off/commands/release.md +311 -0
- package/dist/learning-off/commands/research.md +229 -0
- package/dist/learning-off/commands/resolve.md +842 -0
- package/dist/learning-off/commands/self-review.md +266 -0
- package/dist/skills/git/references/learn-conventions.md +27 -3
- package/dist/skills/git/references/pr/check-ci-status.md +19 -0
- package/dist/skills/git/references/pr/check-merge-readiness.md +19 -0
- package/dist/skills/git/references/pr/fetch-review-threads.md +30 -0
- package/dist/skills/git/references/pr/post-resolution-summary.md +18 -0
- package/dist/skills/git/references/pr/post-review-summary.md +20 -0
- package/dist/skills/git/references/pr/resolve-review-threads.md +28 -4
- package/dist/skills/git/references/pr/update-pr-evidence.md +13 -0
- package/dist/skills/git/references/pr/validate-branch.md +26 -0
- package/dist/skills/git/references/tracker/_contract.md +33 -0
- package/dist/skills/git/references/tracker/github/associate-release.md +24 -0
- package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +31 -1
- package/dist/skills/git/references/tracker/github/create-release.md +30 -1
- package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +89 -2
- package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +16 -1
- package/dist/skills/git/references/tracker/github/fetch-issue.md +38 -1
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +49 -1
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +29 -0
- package/dist/skills/git/references/tracker/github/manage-debt.md +21 -1
- package/dist/skills/git/references/tracker/github/post-wave-report.md +17 -1
- package/dist/skills/git/references/tracker/github/setup-task.md +53 -1
- package/dist/skills/git/references/tracker/jira/associate-release.md +25 -1
- package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +31 -1
- package/dist/skills/git/references/tracker/jira/create-release.md +30 -1
- package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +89 -2
- package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +16 -1
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +38 -1
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +49 -1
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +29 -0
- package/dist/skills/git/references/tracker/jira/manage-debt.md +21 -1
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +17 -1
- package/dist/skills/git/references/tracker/jira/setup-task.md +55 -3
- package/dist/skills/git/references/tracker/linear/associate-release.md +25 -1
- package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +31 -1
- package/dist/skills/git/references/tracker/linear/create-release.md +30 -1
- package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +89 -2
- package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +16 -1
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +38 -1
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +49 -1
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +29 -0
- package/dist/skills/git/references/tracker/linear/manage-debt.md +21 -1
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +17 -1
- package/dist/skills/git/references/tracker/linear/setup-task.md +55 -3
- package/dist/targets/claude-code/agent-type-owner.js +164 -0
- package/dist/targets/claude-code/behavior-install.js +111 -0
- package/dist/targets/claude-code/installer.js +140 -37
- package/dist/targets/claude-code/language-stamp.js +185 -0
- package/dist/targets/claude-code/learning-install.js +497 -0
- package/dist/targets/claude-code/tracker-install.js +33 -16
- package/dist/targets/claude-code/tracker-servers.js +107 -0
- package/package.json +4 -3
- package/src/assets/agents/_partials/_code.mds +109 -0
- package/src/assets/agents/code-edit.mds +81 -0
- package/src/assets/agents/code-fix.mds +113 -0
- package/src/assets/agents/code.mds +135 -0
- package/src/assets/agents/design.mds +148 -0
- package/src/assets/agents/diagnose.mds +225 -0
- package/src/assets/agents/evaluate.md +8 -4
- package/src/assets/agents/explore.md +58 -0
- package/src/assets/agents/git.mds +33 -722
- package/src/assets/agents/knowledge.mds +131 -0
- package/src/assets/agents/learning.md +19 -69
- package/src/assets/agents/research.mds +182 -0
- package/src/assets/agents/review.mds +282 -0
- package/src/assets/agents/scrutinize.mds +135 -0
- package/src/assets/agents/simplify.md +3 -0
- package/src/assets/agents/skim.mds +165 -0
- package/src/assets/agents/synthesize.md +26 -352
- package/src/assets/agents/test.md +8 -0
- package/src/assets/agents/tracker.md +20 -7
- package/src/assets/agents/triage.mds +201 -0
- package/src/assets/agents/validate.md +13 -6
- package/src/assets/commands/_partials/_compliance.mds +41 -4
- package/src/assets/commands/_partials/_decisions.mds +33 -2
- package/src/assets/commands/_partials/_engine.mds +14 -6
- package/src/assets/commands/_partials/_knowledge.mds +25 -12
- package/src/assets/commands/_partials/_preamble.mds +34 -10
- package/src/assets/commands/_partials/_publication.mds +5 -4
- package/src/assets/commands/_partials/_roster.mds +4 -3
- package/src/assets/commands/_partials/_settings.mds +13 -5
- package/src/assets/commands/_partials/_wave.mds +9 -1
- package/src/assets/commands/bug-analysis.mds +27 -5
- package/src/assets/commands/code-review.mds +158 -52
- package/src/assets/commands/debug.mds +41 -3
- package/src/assets/commands/dynamic-build.mds +47 -27
- package/src/assets/commands/dynamic-plan.mds +40 -9
- package/src/assets/commands/dynamic-profile.mds +9 -1
- package/src/assets/commands/dynamic-tickets.mds +21 -6
- package/src/assets/commands/explore.mds +28 -2
- package/src/assets/commands/implement.mds +126 -129
- package/src/assets/commands/plan.mds +58 -14
- package/src/assets/commands/{release.md → release.mds} +30 -26
- package/src/assets/commands/research.mds +42 -11
- package/src/assets/commands/resolve.mds +59 -15
- package/src/assets/commands/self-review.mds +30 -5
- package/src/assets/mds/.mdsroot +4 -0
- package/src/assets/mds/git/_ops.mds +383 -0
- package/src/assets/mds/git/_ops_pr.mds +201 -0
- package/src/assets/mds/git/_ops_steps.mds +147 -0
- package/src/assets/mds/git/_pr.mds +39 -44
- package/src/assets/mds/git/_references.mds +6 -3
- package/src/assets/mds/tracker/_common.mds +5 -5
- package/src/assets/mds/tracker/_contract.mds +73 -0
- package/src/assets/mds/tracker/_github.mds +73 -16
- package/src/assets/mds/tracker/_jira.mds +77 -19
- package/src/assets/mds/tracker/_linear.mds +77 -19
- package/src/assets/mds/tracker/_mcp.mds +2 -2
- package/src/assets/mds/tracker/_steps.mds +98 -0
- package/src/assets/rules/behavior.md +6 -0
- package/src/assets/rules/context-economy.md +10 -0
- package/src/assets/rules/go.md +1 -0
- package/src/assets/rules/java.md +1 -0
- package/src/assets/rules/python.md +1 -0
- package/src/assets/rules/rust.md +1 -0
- package/src/assets/rules/typescript.md +1 -0
- package/src/assets/scripts/claude-md-audit.cjs +611 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +3 -3
- package/src/assets/scripts/hooks/json-helper.cjs +13 -5
- package/src/assets/scripts/hooks/json-parse +34 -10
- package/src/assets/scripts/hooks/lib/project-paths.cjs +0 -6
- package/src/assets/scripts/hooks/session-start-context +315 -7
- package/src/assets/skills/agent-references/SKILL.md +33 -0
- package/src/assets/skills/agent-references/references/learning/maintain.md +71 -0
- package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +71 -0
- package/src/assets/skills/agent-references/references/synthesize/design.md +36 -0
- package/src/assets/skills/agent-references/references/synthesize/exploration.md +38 -0
- package/src/assets/skills/agent-references/references/synthesize/planning.md +60 -0
- package/src/assets/skills/agent-references/references/synthesize/research.md +61 -0
- package/src/assets/skills/agent-references/references/synthesize/review.md +76 -0
- package/src/assets/skills/apply-decisions/SKILL.md +1 -1
- package/src/assets/skills/apply-feature-knowledge/SKILL.md +5 -5
- package/src/assets/skills/compliance/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +80 -83
- package/src/assets/skills/docs-framework/references/patterns.md +14 -25
- package/src/assets/skills/docs-framework/references/violations.md +7 -7
- package/src/assets/skills/feature-knowledge/SKILL.md +43 -12
- package/src/assets/skills/gap-analysis/SKILL.md +0 -16
- package/src/assets/skills/gap-analysis/references/compliance.md +19 -0
- package/src/assets/skills/quality-gates/SKILL.md +1 -1
- package/dist/skills/git/references/pr/ensure-pr-ready.md +0 -24
- package/src/assets/agents/code.md +0 -330
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* beside init.ts in src/cli/commands/ rather than in src/core/ (which holds
|
|
13
13
|
* agent-neutral, target-agnostic utilities).
|
|
14
14
|
*/
|
|
15
|
-
import { resolveExistingViewMode, settingHoldsManagedShape, FLAG_REGISTRY, defaultValueOf, readViewMode, } from '../../core/flags.js';
|
|
15
|
+
import { resolveExistingViewMode, settingHoldsManagedShape, respectUserOwnedSettings, FLAG_REGISTRY, defaultValueOf, readViewMode, } from '../../core/flags.js';
|
|
16
16
|
import { partitionSelectablePlugins } from '../../core/plugins.js';
|
|
17
17
|
import { DEFAULT_TRACKER_PROVIDER } from '../../core/tracker.js';
|
|
18
18
|
/** Registry defaults — all features enabled except proxy (advanced-only, off by default). */
|
|
@@ -24,6 +24,7 @@ export const FEATURE_DEFAULTS = {
|
|
|
24
24
|
learning: true,
|
|
25
25
|
rules: true,
|
|
26
26
|
proxy: false,
|
|
27
|
+
behavior: false,
|
|
27
28
|
compliance: { enabled: false, frameworks: [] },
|
|
28
29
|
tracker: { provider: DEFAULT_TRACKER_PROVIDER },
|
|
29
30
|
};
|
|
@@ -44,6 +45,8 @@ export function resolveSeedFeatures(manifest) {
|
|
|
44
45
|
const hud = manifest?.features.hud ?? FEATURE_DEFAULTS.hud;
|
|
45
46
|
const rules = manifest?.features.rules ?? FEATURE_DEFAULTS.rules;
|
|
46
47
|
const proxy = manifest?.features.proxy ?? FEATURE_DEFAULTS.proxy;
|
|
48
|
+
// D-BEHAVIOR-OPT-IN: a manifest from before the switch has no key — it seeds the default (off).
|
|
49
|
+
const behavior = manifest?.features.behavior ?? FEATURE_DEFAULTS.behavior;
|
|
47
50
|
// Return a fresh object so callers never hold a reference to FEATURE_DEFAULTS.compliance.
|
|
48
51
|
// Without the spread, `manifest?.features.compliance ?? FEATURE_DEFAULTS.compliance` returns
|
|
49
52
|
// the module-level default by reference — downstream mutation would corrupt it process-wide.
|
|
@@ -57,7 +60,7 @@ export function resolveSeedFeatures(manifest) {
|
|
|
57
60
|
const memory = manifest?.features.memory ?? FEATURE_DEFAULTS.memory;
|
|
58
61
|
const knowledge = manifest?.features.knowledge ?? FEATURE_DEFAULTS.knowledge;
|
|
59
62
|
const learning = manifest?.features.learning ?? FEATURE_DEFAULTS.learning;
|
|
60
|
-
return { ambient, memory, hud, knowledge, learning, rules, proxy, compliance, tracker };
|
|
63
|
+
return { ambient, memory, hud, knowledge, learning, rules, proxy, behavior, compliance, tracker };
|
|
61
64
|
}
|
|
62
65
|
/**
|
|
63
66
|
* Resolve the flag record for the init seed.
|
|
@@ -233,7 +236,7 @@ export function resolveInitSeed(seedManifest, settingsSnapshot, plugins) {
|
|
|
233
236
|
// seedManifest?.features.flags may be absent at runtime on very old manifests not yet
|
|
234
237
|
// healed — ?? null collapses to fresh-install behavior (all flags at registry defaults).
|
|
235
238
|
const manifestFlags = seedManifest?.features.flags ?? null;
|
|
236
|
-
const flags = resolveSeedFlags(manifestFlags);
|
|
239
|
+
const flags = respectUserOwnedSettings(resolveSeedFlags(manifestFlags), manifestFlags, settingsSnapshot);
|
|
237
240
|
const manifestPlugins = seedManifest !== null ? seedManifest.plugins : null;
|
|
238
241
|
const { workflowPlugins, languagePlugins } = resolveSeedPlugins(manifestPlugins, seedManifest?.knownPlugins, plugins);
|
|
239
242
|
// Encode the resolved view mode into flags['view-mode'] (all flag state in FlagsRecord).
|
|
@@ -312,6 +315,7 @@ export function applyCliToggles(base, toggles) {
|
|
|
312
315
|
learning: toggles.learning ?? base.learning,
|
|
313
316
|
rules: toggles.rules ?? base.rules,
|
|
314
317
|
proxy: toggles.proxy ?? base.proxy,
|
|
318
|
+
behavior: toggles.behavior ?? base.behavior,
|
|
315
319
|
compliance: toggles.compliance ?? base.compliance,
|
|
316
320
|
tracker: toggles.tracker ?? base.tracker,
|
|
317
321
|
};
|
|
@@ -39,10 +39,14 @@ import { removeManagedDenyList, describeManagedDenyRemoval } from './security.js
|
|
|
39
39
|
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs, resolvePluginsToInstall } from './init-seed.js';
|
|
40
40
|
import { parseFrameworkList, normalizeFrameworks } from '../../core/compliance.js';
|
|
41
41
|
import { formatComplianceSummary, shouldRunComplianceStep, runComplianceStep, buildClackCompliancePrompts, } from './compliance-prompts.js';
|
|
42
|
-
import {
|
|
42
|
+
import { shouldRunBehaviorStep, runBehaviorStep, buildClackBehaviorPrompts } from './behavior-prompts.js';
|
|
43
|
+
import { convergeBehaviorFromManifest } from '../../targets/claude-code/behavior-install.js';
|
|
44
|
+
import { applyTrackerSentinel, effectiveTrackerServers, parseTrackerId, rearmTrackerInference, DEFAULT_TRACKER_PROVIDER, } from '../../core/tracker.js';
|
|
43
45
|
import { formatTrackerSummary, shouldRunTrackerStep, runTrackerStep, buildClackTrackerPrompts, } from './tracker-prompts.js';
|
|
44
46
|
import { shouldRunAttributionStep, runAttributionStep, buildClackAttributionPrompts, applyAttributionAnswer, attributionSeedFrom, } from './attribution-prompts.js';
|
|
45
47
|
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
48
|
+
import { convergeLearningVariants } from '../../targets/claude-code/learning-install.js';
|
|
49
|
+
import { auditAndRecordClaudeMd, formatClaudeMdAuditNote, formatClaudeMdAuditUnavailable, } from '../../core/claude-md-audit.js';
|
|
46
50
|
import * as os from 'os';
|
|
47
51
|
// Re-export pure functions for tests (canonical source is post-install.ts)
|
|
48
52
|
export { substituteSettingsTemplate, mergeDenyList, discoverProjectGitRoots } from '../../targets/claude-code/post-install.js';
|
|
@@ -319,11 +323,29 @@ export function trackerOverrideMessage(provider) {
|
|
|
319
323
|
text: `Tracker: ${formatTrackerSummary(provider)}`,
|
|
320
324
|
};
|
|
321
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* The audit's printable outcome, the ONE function both init paths use.
|
|
328
|
+
*
|
|
329
|
+
* D-CLAUDE-MD-IMPORT-AUDIT, D-INIT-REAL-OUTCOME: the Recommended summary note prints
|
|
330
|
+
* before the install runs, and the Advanced path prints no end-of-wizard summary
|
|
331
|
+
* (D-TRACKER-CLI-SURFACE), so the audit cannot be a row of either. It is decided after
|
|
332
|
+
* the install, from the CLAUDE.md files as they are, and printed as one note by the single
|
|
333
|
+
* call site in `run`, whichever path got there: the lines are the script's own formatter's,
|
|
334
|
+
* the ones the SessionStart hook shows in its systemMessage. Nothing is printed when
|
|
335
|
+
* nothing is flagged; a failure is at most one degraded line and never changes the exit code.
|
|
336
|
+
*
|
|
337
|
+
* Pure — returns text, prints nothing.
|
|
338
|
+
*/
|
|
339
|
+
export function claudeMdAuditStep(outcome) {
|
|
340
|
+
if (!outcome.ok)
|
|
341
|
+
return { note: null, degraded: formatClaudeMdAuditUnavailable(outcome.error) };
|
|
342
|
+
return { note: formatClaudeMdAuditNote(outcome.value), degraded: null };
|
|
343
|
+
}
|
|
322
344
|
/** The real adapter — the ONE binding of each owner into the init lifecycle. */
|
|
323
345
|
export function buildTrackerLifecycleIO() {
|
|
324
346
|
return {
|
|
325
347
|
writeManifest,
|
|
326
|
-
convergeArtifacts: (claudeDir, warn) => convergeTrackerArtifacts({ claudeDir, warn }),
|
|
348
|
+
convergeArtifacts: (claudeDir, servers, warn) => convergeTrackerArtifacts({ claudeDir, servers, warn }),
|
|
327
349
|
rearmInference: rearmTrackerInference,
|
|
328
350
|
applySentinel: applyTrackerSentinel,
|
|
329
351
|
};
|
|
@@ -376,7 +398,7 @@ export async function persistManifestThenConvergeTracker(opts) {
|
|
|
376
398
|
// The Tracker agent file — installed on every machine (D-INSTALL-ALL-PROVIDERS),
|
|
377
399
|
// because a repository can select a provider the machine never did.
|
|
378
400
|
const agentWarnings = [];
|
|
379
|
-
const artifacts = await io.convergeArtifacts(claudeDir, (msg) => agentWarnings.push(msg));
|
|
401
|
+
const artifacts = await io.convergeArtifacts(claudeDir, effectiveTrackerServers(manifestData.features.tracker), (msg) => agentWarnings.push(msg));
|
|
380
402
|
for (const text of agentWarnings)
|
|
381
403
|
messages.push({ level: 'warn', text });
|
|
382
404
|
// D-TRACKER-PARALLEL: the two owners touch disjoint files — the attempt
|
|
@@ -475,6 +497,7 @@ export function buildHudOnlyManifest(existing, version, now) {
|
|
|
475
497
|
features: {
|
|
476
498
|
ambient: false, memory: false, hud: true, knowledge: false,
|
|
477
499
|
learning: false, rules: false, flags: {}, proxy: false,
|
|
500
|
+
behavior: false,
|
|
478
501
|
compliance: { enabled: false, frameworks: [] },
|
|
479
502
|
// The exported default, not a literal — the one constant every other
|
|
480
503
|
// module reads, so a moved default moves here too.
|
|
@@ -578,6 +601,8 @@ export const initCommand = new Command('init')
|
|
|
578
601
|
.option('--no-rules', 'Disable rules')
|
|
579
602
|
.option('--proxy', 'Enable external model routing (GPT models via your OpenAI/Codex subscription)')
|
|
580
603
|
.option('--no-proxy', 'Disable external model routing')
|
|
604
|
+
.option('--behavior', 'Enable the behavior rule (agents reply in ASD-STE100 Simplified Technical English; off by default)')
|
|
605
|
+
.option('--no-behavior', 'Disable the behavior rule')
|
|
581
606
|
.option('--compliance <list>', 'Enable compliance with comma-separated framework IDs (e.g., gdpr,hipaa)')
|
|
582
607
|
.option('--no-compliance', 'Disable compliance (removes the rule; the skill and framework references stay installed; frameworks remembered for re-enable)')
|
|
583
608
|
.option('--tracker <id>', 'The machine\'s default issue tracker provider: github, jira, or linear')
|
|
@@ -669,7 +694,7 @@ export const initCommand = new Command('init')
|
|
|
669
694
|
// ── Hoisted reads: the prior state that seeds the prompts (InitSeed, Phase 4) ──
|
|
670
695
|
let existingManifest = null;
|
|
671
696
|
try {
|
|
672
|
-
existingManifest = await readManifest(devflowDir);
|
|
697
|
+
existingManifest = await readManifest(devflowDir, { onWarning: (message) => p.log.warn(message) });
|
|
673
698
|
}
|
|
674
699
|
catch { /* unreadable manifest — seeded as a fresh install */ }
|
|
675
700
|
// D-INIT-NOT-HOME (same-location.ts): a repository rooted at HOME is no
|
|
@@ -895,6 +920,10 @@ export const initCommand = new Command('init')
|
|
|
895
920
|
// Fresh installs → false (FEATURE_DEFAULTS.proxy). Re-inits → prior manifest value.
|
|
896
921
|
// --reset → false (resolveResetGatedInputs null-seeds the manifest).
|
|
897
922
|
let proxyEnabled = seed.features.proxy;
|
|
923
|
+
// behavior (D-BEHAVIOR-OPT-IN): opt-in, manifest-group like proxy. Fresh installs and --reset
|
|
924
|
+
// → false (FEATURE_DEFAULTS.behavior); re-inits and the Recommended path keep the prior
|
|
925
|
+
// manifest value; only --behavior/--no-behavior and the Advanced wizard change it.
|
|
926
|
+
let behaviorEnabled = seed.features.behavior;
|
|
898
927
|
// compliance: manifest-group (like proxy); seed from prior manifest value.
|
|
899
928
|
// CLI override applied below in both Recommended and Advanced paths.
|
|
900
929
|
let complianceEnabled = seed.features.compliance.enabled;
|
|
@@ -902,6 +931,10 @@ export const initCommand = new Command('init')
|
|
|
902
931
|
// tracker: manifest-group (like proxy and compliance); seed from prior manifest.
|
|
903
932
|
// CLI override applied below in both Recommended and Advanced paths.
|
|
904
933
|
let trackerProvider = seed.features.tracker.provider;
|
|
934
|
+
// The tracker tool servers (D-TRACKER-SERVER-ALLOWLIST) are set by `devflow tracker --servers`
|
|
935
|
+
// alone, so init carries the prior value through untouched: it stamps the installed agents
|
|
936
|
+
// with it and writes it back to the manifest. Absent stays absent (the default list).
|
|
937
|
+
const trackerServers = seed.features.tracker.servers;
|
|
905
938
|
let enabledFlags = { ...seed.flags };
|
|
906
939
|
// viewModeExplicit: true when --reset is passed; signals resolveFinalViewMode to let the
|
|
907
940
|
// seed-time view-mode win over an externally-set value in settings.json.
|
|
@@ -1012,6 +1045,7 @@ export const initCommand = new Command('init')
|
|
|
1012
1045
|
learning: options.learning,
|
|
1013
1046
|
rules: options.rules,
|
|
1014
1047
|
proxy: options.proxy,
|
|
1048
|
+
behavior: options.behavior,
|
|
1015
1049
|
compliance: cliComplianceOverride ?? wizardCompliance,
|
|
1016
1050
|
// Precedence: cliOverride ?? wizardResult ?? seed (applyCliToggles supplies the seed arm).
|
|
1017
1051
|
tracker: cliTrackerOverride ?? wizardTracker,
|
|
@@ -1023,6 +1057,7 @@ export const initCommand = new Command('init')
|
|
|
1023
1057
|
learningEnabled = effectiveFeatures.learning;
|
|
1024
1058
|
rulesEnabled = effectiveFeatures.rules;
|
|
1025
1059
|
proxyEnabled = effectiveFeatures.proxy;
|
|
1060
|
+
behaviorEnabled = effectiveFeatures.behavior;
|
|
1026
1061
|
complianceEnabled = effectiveFeatures.compliance.enabled;
|
|
1027
1062
|
complianceFrameworks = effectiveFeatures.compliance.frameworks;
|
|
1028
1063
|
trackerProvider = effectiveFeatures.tracker.provider;
|
|
@@ -1063,6 +1098,7 @@ export const initCommand = new Command('init')
|
|
|
1063
1098
|
`HUD: ${hudEnabled ? 'enabled' : 'disabled'}`,
|
|
1064
1099
|
`Knowledge bases: ${knowledgeEnabled ? 'enabled' : 'disabled'}`,
|
|
1065
1100
|
`Ext model routing: ${proxyEnabled ? 'enabled' : 'disabled'}`,
|
|
1101
|
+
`Behavior rule: ${behaviorEnabled ? 'enabled' : 'disabled'}`,
|
|
1066
1102
|
`Compliance: ${complianceSummary}`,
|
|
1067
1103
|
// Recommended emits no per-step outcome lines, so this summary row is the
|
|
1068
1104
|
// tracker step's ONLY surface on this path — both surfaces or it is
|
|
@@ -1194,6 +1230,33 @@ export const initCommand = new Command('init')
|
|
|
1194
1230
|
}
|
|
1195
1231
|
rulesEnabled = rulesChoice;
|
|
1196
1232
|
}
|
|
1233
|
+
// Behavior rule (Advanced-only; opt-in, default OFF; D-BEHAVIOR-OPT-IN). Runs after the
|
|
1234
|
+
// rules step because the rule installs only while rules are on, and the gate knows it.
|
|
1235
|
+
// A skipped gate leaves the seeded value, which --behavior/--no-behavior replace.
|
|
1236
|
+
if (shouldRunBehaviorStep({
|
|
1237
|
+
isTTY: process.stdin.isTTY,
|
|
1238
|
+
hasCliOverride: options.behavior !== undefined,
|
|
1239
|
+
rulesEnabled,
|
|
1240
|
+
})) {
|
|
1241
|
+
const behaviorStep = await runBehaviorStep({
|
|
1242
|
+
seed: behaviorEnabled,
|
|
1243
|
+
prompts: buildClackBehaviorPrompts(),
|
|
1244
|
+
});
|
|
1245
|
+
if (behaviorStep.kind === 'cancelled') {
|
|
1246
|
+
p.cancel('Installation cancelled.');
|
|
1247
|
+
process.exit(0);
|
|
1248
|
+
}
|
|
1249
|
+
behaviorEnabled = behaviorStep.enabled;
|
|
1250
|
+
for (const msg of behaviorStep.messages) {
|
|
1251
|
+
if (msg.level === 'success')
|
|
1252
|
+
p.log.success(msg.text);
|
|
1253
|
+
else
|
|
1254
|
+
p.log.info(msg.text);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
else if (options.behavior !== undefined) {
|
|
1258
|
+
behaviorEnabled = options.behavior;
|
|
1259
|
+
}
|
|
1197
1260
|
// External model routing (Advanced-only; default OFF; never part of Recommended)
|
|
1198
1261
|
if (options.proxy !== undefined) {
|
|
1199
1262
|
proxyEnabled = options.proxy;
|
|
@@ -1558,6 +1621,11 @@ export const initCommand = new Command('init')
|
|
|
1558
1621
|
rulesMap,
|
|
1559
1622
|
isPartialInstall: !!options.plugin,
|
|
1560
1623
|
spinner: s,
|
|
1624
|
+
// The SETTLED switch, after the flag, the prompt and the seed have all had
|
|
1625
|
+
// their say (D-LEARNING-VARIANT-INSTALL): the files installed here and the
|
|
1626
|
+
// manifest written at the end of init cannot disagree about it.
|
|
1627
|
+
learning: learningEnabled,
|
|
1628
|
+
trackerServers,
|
|
1561
1629
|
// Non-fatal install notices with no other channel (skipped symlinks in the
|
|
1562
1630
|
// generated reference tree, mode-normalisation failures) reach the user rather
|
|
1563
1631
|
// than the void. Collected now, emitted after the spinner stops.
|
|
@@ -1569,6 +1637,30 @@ export const initCommand = new Command('init')
|
|
|
1569
1637
|
p.log.error(`${error}`);
|
|
1570
1638
|
process.exit(1);
|
|
1571
1639
|
}
|
|
1640
|
+
// D-LEARNING-VARIANT-INSTALL: converge the learning variants. The file copy above
|
|
1641
|
+
// installed the variant of everything IT installed; on a `--plugin` install the
|
|
1642
|
+
// other plugins' files, already on disk, may still be the other variant (this run
|
|
1643
|
+
// may have flipped the switch), and the apply-decisions skill is removed here when
|
|
1644
|
+
// the copy skipped it on a partial install. Rewrites only what is installed.
|
|
1645
|
+
// Warn-not-abort: either mismatch is safe (an on variant is still gated at run
|
|
1646
|
+
// time, an off variant loads no decisions). The agent mapping is reapplied below.
|
|
1647
|
+
try {
|
|
1648
|
+
const learningConverge = await convergeLearningVariants({
|
|
1649
|
+
claudeDir,
|
|
1650
|
+
devflowDir,
|
|
1651
|
+
learning: learningEnabled,
|
|
1652
|
+
plugins: effectivePlugins,
|
|
1653
|
+
warn: (msg) => installWarnings.push(msg),
|
|
1654
|
+
});
|
|
1655
|
+
if (verbose && learningConverge.converged) {
|
|
1656
|
+
p.log.info(`Learning ${learningEnabled ? 'on' : 'off'} variants: ` +
|
|
1657
|
+
`${learningConverge.commandsRewritten.length} command(s) and ${learningConverge.agentsRewritten.length} agent(s) ` +
|
|
1658
|
+
`rewritten, ${learningConverge.unchanged} already current`);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
catch (err) {
|
|
1662
|
+
installWarnings.push(`Learning variant convergence failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
1663
|
+
}
|
|
1572
1664
|
// Converge compliance artifacts (always converge, never short-circuit).
|
|
1573
1665
|
// Called unconditionally so that enabling/disabling compliance during init
|
|
1574
1666
|
// is reflected in the installed artifacts without a separate devflow compliance run.
|
|
@@ -1595,6 +1687,25 @@ export const initCommand = new Command('init')
|
|
|
1595
1687
|
p.log.warn(`Compliance artifact convergence failed — install succeeded but compliance artifacts may be stale: ` +
|
|
1596
1688
|
`${err instanceof Error ? err.message : String(err)}`);
|
|
1597
1689
|
}
|
|
1690
|
+
// D-BEHAVIOR-OPT-IN: converge the behavior rule. The plugin installer never copies it (no
|
|
1691
|
+
// plugin declares it) and a full install wiped rules/devflow/ above, so this is the one
|
|
1692
|
+
// writer: it installs the file when the switch is on and rules are on, and removes it
|
|
1693
|
+
// otherwise. Warn-not-abort, like the compliance convergence.
|
|
1694
|
+
try {
|
|
1695
|
+
await convergeBehaviorFromManifest({
|
|
1696
|
+
claudeDir,
|
|
1697
|
+
devflowDir,
|
|
1698
|
+
manifest: { features: { behavior: behaviorEnabled, rules: rulesEnabled } },
|
|
1699
|
+
warn: (msg) => p.log.warn(msg),
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
catch (err) {
|
|
1703
|
+
p.log.warn(`Behavior rule convergence failed — install succeeded but the behavior rule may be stale: ` +
|
|
1704
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
1705
|
+
}
|
|
1706
|
+
if (behaviorEnabled && !rulesEnabled) {
|
|
1707
|
+
p.log.info('Behavior rule is on but rules are disabled, so it is not installed — run `devflow rules --enable`.');
|
|
1708
|
+
}
|
|
1598
1709
|
// Clean up stale skills from previous installations
|
|
1599
1710
|
s.message('Cleaning up');
|
|
1600
1711
|
const skillsDir = path.join(claudeDir, 'skills');
|
|
@@ -2201,6 +2312,10 @@ export const initCommand = new Command('init')
|
|
|
2201
2312
|
security: securityMode,
|
|
2202
2313
|
// Final resolved value — may be forced off by preflight failure.
|
|
2203
2314
|
proxy: proxyEnabled,
|
|
2315
|
+
// Resolved behavior switch (D-BEHAVIOR-OPT-IN) — seeded from the prior manifest,
|
|
2316
|
+
// overridden by --behavior/--no-behavior and the Advanced wizard. The rule file was
|
|
2317
|
+
// converged above to match it.
|
|
2318
|
+
behavior: behaviorEnabled,
|
|
2204
2319
|
// Resolved compliance state — seeded from prior manifest, overridden by CLI flags
|
|
2205
2320
|
// and Advanced wizard selection. convergeComplianceArtifacts was called above.
|
|
2206
2321
|
// normalizeFrameworks: dedup + filter unknowns before persisting.
|
|
@@ -2208,7 +2323,7 @@ export const initCommand = new Command('init')
|
|
|
2208
2323
|
// Resolved tracker selection. Already a validated TrackerProvider — it came
|
|
2209
2324
|
// through parseTrackerId (CLI), the typed wizard select, or the seed, which
|
|
2210
2325
|
// itself came through normalizeTrackerFeature on read.
|
|
2211
|
-
tracker: { provider: trackerProvider },
|
|
2326
|
+
tracker: { provider: trackerProvider, ...(trackerServers !== undefined ? { servers: trackerServers } : {}) },
|
|
2212
2327
|
},
|
|
2213
2328
|
installedAt: existingManifest?.installedAt ?? now,
|
|
2214
2329
|
updatedAt: now,
|
|
@@ -2263,6 +2378,20 @@ export const initCommand = new Command('init')
|
|
|
2263
2378
|
agent: trackerLifecycle.agent,
|
|
2264
2379
|
});
|
|
2265
2380
|
logSummaryLines(trackerLines);
|
|
2381
|
+
// CLAUDE.md import audit (D-CLAUDE-MD-IMPORT-AUDIT, D-AUDIT-STAMP): after the install, on
|
|
2382
|
+
// both paths, through claudeMdAuditStep. Global roots always; the project's roots when init
|
|
2383
|
+
// runs inside a git repository that is not HOME, at its toplevel. The stamp is written here,
|
|
2384
|
+
// now that the machine root exists, so the first SessionStart repeats nothing printed below.
|
|
2385
|
+
const auditStep = claudeMdAuditStep(await auditAndRecordClaudeMd({
|
|
2386
|
+
claudeDir,
|
|
2387
|
+
projectRoot: gitRoot,
|
|
2388
|
+
home: homeDir,
|
|
2389
|
+
devflowDir,
|
|
2390
|
+
}));
|
|
2391
|
+
if (auditStep.note !== null)
|
|
2392
|
+
p.note(auditStep.note, 'CLAUDE.md import audit');
|
|
2393
|
+
if (auditStep.degraded !== null)
|
|
2394
|
+
p.log.warn(auditStep.degraded);
|
|
2266
2395
|
// External model routing status line (Advanced path / explicit --proxy flag only)
|
|
2267
2396
|
if (proxyEnabled) {
|
|
2268
2397
|
p.log.info(`External model routing: ${color.green('enabled')} — takes effect in new Claude Code sessions`);
|
|
@@ -7,7 +7,8 @@ import { getLearningDir, getLearningTuningConfigPath, } from '../../core/project
|
|
|
7
7
|
import { loadShippedAgentDefaults } from '../../core/agent-models.js';
|
|
8
8
|
import { readMachineFeature, writeMachineFeature } from '../../core/feature-switch.js';
|
|
9
9
|
import { loadSettingsModule, narrowedSwitchLabel, personalConfigTrackedWarning } from '../../core/evidence-policy.js';
|
|
10
|
-
import { getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
10
|
+
import { getClaudeDirectory, getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
11
|
+
import { applyLearningToggle, describeLearningConverge, readRunningVersion, } from '../../targets/claude-code/learning-install.js';
|
|
11
12
|
import { getLedgerRoot } from '../../core/ledger-root.js';
|
|
12
13
|
import { drainLearningQueue } from '../../core/learning-queue-cleanup.js';
|
|
13
14
|
import { firstSymbolicLink } from '../../core/linked-path.js';
|
|
@@ -426,11 +427,44 @@ async function handleClear() {
|
|
|
426
427
|
if (!drain.drained)
|
|
427
428
|
p.log.warn(formatRefusedDrain('learning', drain.linkedFolder));
|
|
428
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Make the installed prompts match the switch just written
|
|
432
|
+
* (D-LEARNING-VARIANT-INSTALL): the learning-on or learning-off commands and
|
|
433
|
+
* agents, and the apply-decisions skill. The same convergence `devflow init` runs,
|
|
434
|
+
* followed by reapplying the saved agent model mapping so `devflow agents`
|
|
435
|
+
* overrides survive.
|
|
436
|
+
*
|
|
437
|
+
* Warn-not-fail: the switch itself is already recorded, and either mismatch is
|
|
438
|
+
* safe (an on variant is still gated at run time, an off variant loads no
|
|
439
|
+
* decisions), so a problem here never changes the exit code.
|
|
440
|
+
*/
|
|
441
|
+
async function convergeInstalledVariants(enabled) {
|
|
442
|
+
try {
|
|
443
|
+
const outcome = await applyLearningToggle({
|
|
444
|
+
claudeDir: getClaudeDirectory(),
|
|
445
|
+
devflowDir: getDevFlowDirectory(),
|
|
446
|
+
learning: enabled,
|
|
447
|
+
runningVersion: await readRunningVersion(),
|
|
448
|
+
warn: (msg) => p.log.warn(msg),
|
|
449
|
+
});
|
|
450
|
+
if (outcome.kind === 'skipped') {
|
|
451
|
+
p.log.warn(outcome.message);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const changed = describeLearningConverge(outcome.result, enabled);
|
|
455
|
+
if (changed !== null)
|
|
456
|
+
p.log.info(color.dim(`Installed prompts: ${changed}`));
|
|
457
|
+
}
|
|
458
|
+
catch (err) {
|
|
459
|
+
p.log.warn(`Could not update the installed prompts for the new learning setting — run ${color.cyan('devflow init')}: ${String(err)}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
429
462
|
/**
|
|
430
463
|
* `--enable` / `--disable`: the machine-wide switch (D-FEATURES-NARROW-ONLY),
|
|
431
464
|
* converged exactly as `devflow init --learning / --no-learning` converges it —
|
|
432
|
-
* the manifest value,
|
|
433
|
-
*
|
|
465
|
+
* the manifest value, the installed prompt variants and skill
|
|
466
|
+
* (D-LEARNING-VARIANT-INSTALL), and on disable a drained queue in the current
|
|
467
|
+
* project. Never requires a git root: the switch is not a per-project setting.
|
|
434
468
|
*/
|
|
435
469
|
async function handleToggle(enabled) {
|
|
436
470
|
const recorded = await writeMachineFeature(getDevFlowDirectory(), 'learning', enabled);
|
|
@@ -439,6 +473,7 @@ async function handleToggle(enabled) {
|
|
|
439
473
|
process.exitCode = 1;
|
|
440
474
|
return;
|
|
441
475
|
}
|
|
476
|
+
await convergeInstalledVariants(enabled);
|
|
442
477
|
if (enabled) {
|
|
443
478
|
p.log.success('Learning enabled in every project (a repository can opt out)');
|
|
444
479
|
p.log.info(color.dim('Architectural decisions and pitfalls will be detected from your sessions'));
|
|
@@ -9,6 +9,7 @@ import { readManifest, writeManifest } from '../../core/manifest.js';
|
|
|
9
9
|
import { rulesDir } from '../../core/assets.js';
|
|
10
10
|
import { installAllRules, validateRuleShadow } from '../../targets/claude-code/installer.js';
|
|
11
11
|
import { convergeFromManifest } from '../../targets/claude-code/compliance-install.js';
|
|
12
|
+
import { convergeBehaviorFromManifest } from '../../targets/claude-code/behavior-install.js';
|
|
12
13
|
/**
|
|
13
14
|
* Check whether a rule has a user shadow file at ~/.devflow/rules/{name}.md.
|
|
14
15
|
*/
|
|
@@ -256,6 +257,21 @@ export async function runRulesEnable(claudeDir, devflowDir) {
|
|
|
256
257
|
// Warn-not-abort — compliance convergence failure does not undo rules install
|
|
257
258
|
p.log.warn(`Compliance rule convergence failed — rules installed but compliance rule may be stale: ${err instanceof Error ? err.message : String(err)}`);
|
|
258
259
|
}
|
|
260
|
+
// D-BEHAVIOR-OPT-IN: the wipe above removed the opt-in behavior rule with the rest of
|
|
261
|
+
// the directory, so bring it back when its switch is on. Same rulesEnabledOverride
|
|
262
|
+
// reasoning as compliance: a failed install leaves no rules to add this one to.
|
|
263
|
+
try {
|
|
264
|
+
await convergeBehaviorFromManifest({
|
|
265
|
+
claudeDir,
|
|
266
|
+
devflowDir,
|
|
267
|
+
manifest,
|
|
268
|
+
warn: (msg) => p.log.warn(msg),
|
|
269
|
+
rulesEnabledOverride: !installFailed,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
p.log.warn(`Behavior rule convergence failed — rules installed but the behavior rule may be stale: ${err instanceof Error ? err.message : String(err)}`);
|
|
274
|
+
}
|
|
259
275
|
}
|
|
260
276
|
export const rulesCommand = new Command('rules')
|
|
261
277
|
.description('Manage Devflow rules')
|
|
@@ -16,18 +16,22 @@
|
|
|
16
16
|
* --set converges the sentinel in BOTH directions, so flipping
|
|
17
17
|
* back to github removes what flipping away wrote.
|
|
18
18
|
* A failed re-arm or sentinel step warns, it never aborts.
|
|
19
|
+
* --servers sets the machine's tracker tool servers (D-TRACKER-SERVER-ALLOWLIST) and
|
|
20
|
+
* re-stamps the installed Git and Tracker agents at once; it is orthogonal to --set and
|
|
21
|
+
* neither flag touches the other's manifest field.
|
|
19
22
|
*/
|
|
20
23
|
import { Command } from 'commander';
|
|
21
24
|
import { promises as fs } from 'fs';
|
|
22
25
|
import * as path from 'path';
|
|
23
26
|
import * as p from '@clack/prompts';
|
|
24
27
|
import color from 'picocolors';
|
|
25
|
-
import { DEFAULT_TRACKER_PROVIDER, TRACKER_ATTEMPTS_MAX, TRACKER_CONVENTIONS_READ_BYTES, TRACKER_PROVIDERS, applyTrackerSentinel, describeTrackerValue, parseTrackerFrontmatter, parseTrackerId, readBoundedHead, rearmTrackerInference, trackerConventionsPath, } from '../../core/tracker.js';
|
|
28
|
+
import { DEFAULT_TRACKER_PROVIDER, TRACKER_ATTEMPTS_MAX, TRACKER_CONVENTIONS_READ_BYTES, TRACKER_PROVIDERS, applyTrackerSentinel, describeTrackerValue, effectiveTrackerServers, parseTrackerFrontmatter, parseTrackerId, parseTrackerServers, readBoundedHead, rearmTrackerInference, trackerConventionsPath, } from '../../core/tracker.js';
|
|
26
29
|
import { readManifest, syncManifestFeature } from '../../core/manifest.js';
|
|
27
30
|
import { loadSettingsModule, personalConfigTrackedWarning, repoTrackerSelection } from '../../core/evidence-policy.js';
|
|
28
31
|
import { getClaudeDirectory, getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
|
|
29
32
|
import { SKILL_REFS_SKILL_NAME, installedReferenceManifest } from '../../core/mds-variants.js';
|
|
30
33
|
import { prefixSkillName } from '../../core/plugins.js';
|
|
34
|
+
import { TRACKER_STAMPED_AGENTS, restampTrackerAgents, } from '../../targets/claude-code/tracker-servers.js';
|
|
31
35
|
/**
|
|
32
36
|
* Read the provenance header of `~/.devflow/tracker/{provider}.md`.
|
|
33
37
|
*
|
|
@@ -112,6 +116,10 @@ export async function readTrackerStatusConventions(devflowDir, provider) {
|
|
|
112
116
|
provenance: await readTrackerProvenance(devflowDir, provider),
|
|
113
117
|
};
|
|
114
118
|
}
|
|
119
|
+
/** Render the tracker servers for the `--status` note. Pure; every name was validated at its boundary. */
|
|
120
|
+
export function formatTrackerServers(list, isDefault) {
|
|
121
|
+
return `${list.join(', ')}${isDefault ? ` ${color.dim('(default)')}` : ''}`;
|
|
122
|
+
}
|
|
115
123
|
/**
|
|
116
124
|
* The `--status` note's lines. Pure.
|
|
117
125
|
*
|
|
@@ -131,7 +139,7 @@ export function formatTrackerStatus(input) {
|
|
|
131
139
|
}
|
|
132
140
|
lines.push(...(input.conventions.kind === 'none'
|
|
133
141
|
? ['Conventions: none (GitHub needs no learned conventions)', 'File: none']
|
|
134
|
-
: [`Conventions: ${formatTrackerProvenance(input.conventions.provenance)}`, `File: ${input.conventions.file}`]), `Mechanics: ${formatTrackerMechanics(input.mechanics)}`, `Inference: ${input.inference}`);
|
|
142
|
+
: [`Conventions: ${formatTrackerProvenance(input.conventions.provenance)}`, `File: ${input.conventions.file}`]), `Mechanics: ${formatTrackerMechanics(input.mechanics)}`, `Servers: ${formatTrackerServers(input.servers.list, input.servers.isDefault)}`, `Inference: ${input.inference}`);
|
|
135
143
|
return lines.join('\n');
|
|
136
144
|
}
|
|
137
145
|
/** The real adapter — the ONE binding of each operation into the `--set` path. */
|
|
@@ -164,7 +172,8 @@ export function buildTrackerSetIO() {
|
|
|
164
172
|
export async function runTrackerSet(opts) {
|
|
165
173
|
const { devflowDir, current, requested, io } = opts;
|
|
166
174
|
const messages = [];
|
|
167
|
-
|
|
175
|
+
// The provider changes; the tracker servers are a different setting and ride along.
|
|
176
|
+
await io.syncManifest(devflowDir, { ...current, provider: requested });
|
|
168
177
|
// [DR-22] a selection change re-arms the attempt counters; [DR-10] the
|
|
169
178
|
// sentinel converges in both directions.
|
|
170
179
|
const rearm = await io.rearmInference(devflowDir);
|
|
@@ -180,18 +189,72 @@ export async function runTrackerSet(opts) {
|
|
|
180
189
|
});
|
|
181
190
|
return { provider: requested, messages };
|
|
182
191
|
}
|
|
192
|
+
/** The real adapter — the ONE binding of each operation into the `--servers` path. */
|
|
193
|
+
export function buildTrackerServersIO() {
|
|
194
|
+
return {
|
|
195
|
+
syncManifest: (devflowDir, state) => syncManifestFeature(devflowDir, 'tracker', state),
|
|
196
|
+
restamp: (claudeDir, servers, warn) => restampTrackerAgents({ claudeDir, servers, warn }),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Set the machine's tracker tool servers.
|
|
201
|
+
*
|
|
202
|
+
* Follows `--set`'s convergence order (D-TRACKER-CONVERGE-SET): the manifest first, then
|
|
203
|
+
* the installed agents, which are re-stamped ONLY when the manifest write landed, so an
|
|
204
|
+
* installed agent never names a list the manifest does not record. Unlike `--set`, a failed
|
|
205
|
+
* manifest write is the whole command failing (there is nothing else for it to do), so it is
|
|
206
|
+
* returned as `ok: false` for the caller to exit on; a failed re-stamp warns, it never aborts.
|
|
207
|
+
*
|
|
208
|
+
* `default` removes the key, so an absent `servers` stays the one spelling of the default.
|
|
209
|
+
* Never throws.
|
|
210
|
+
*/
|
|
211
|
+
export async function runTrackerServers(opts) {
|
|
212
|
+
const { devflowDir, claudeDir, current, requested, io } = opts;
|
|
213
|
+
const messages = [];
|
|
214
|
+
const next = requested.kind === 'default'
|
|
215
|
+
? { provider: current.provider }
|
|
216
|
+
: { provider: current.provider, servers: requested.servers };
|
|
217
|
+
try {
|
|
218
|
+
await io.syncManifest(devflowDir, next);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
messages.push({
|
|
222
|
+
level: 'error',
|
|
223
|
+
text: `Could not write the manifest — the tracker servers were not changed (${err instanceof Error ? err.message : String(err)})`,
|
|
224
|
+
});
|
|
225
|
+
return { ok: false, state: current, messages };
|
|
226
|
+
}
|
|
227
|
+
const servers = effectiveTrackerServers(next);
|
|
228
|
+
const unchanged = servers.join('\n') === effectiveTrackerServers(current).join('\n');
|
|
229
|
+
const label = `${servers.join(', ')}${requested.kind === 'default' ? ' (default)' : ''}`;
|
|
230
|
+
messages.push({ level: unchanged ? 'info' : 'success', text: `Tracker servers: ${label}${unchanged ? ' (unchanged)' : ''}` });
|
|
231
|
+
const restamp = await io.restamp(claudeDir, servers, (message) => messages.push({ level: 'warn', text: message }));
|
|
232
|
+
const done = restamp.rewritten.length + restamp.unchanged.length;
|
|
233
|
+
if (restamp.absent.length === TRACKER_STAMPED_AGENTS.length && restamp.failed.length === 0) {
|
|
234
|
+
messages.push({ level: 'info', text: 'No installed Git or Tracker agent to update — run devflow init' });
|
|
235
|
+
}
|
|
236
|
+
else if (restamp.rewritten.length > 0) {
|
|
237
|
+
messages.push({ level: 'info', text: `Updated the installed ${restamp.rewritten.join(' and ')} agent${restamp.rewritten.length > 1 ? 's' : ''}` });
|
|
238
|
+
}
|
|
239
|
+
else if (done > 0 && restamp.failed.length === 0) {
|
|
240
|
+
messages.push({ level: 'info', text: 'The installed agents already carry this list' });
|
|
241
|
+
}
|
|
242
|
+
return { ok: true, state: next, messages };
|
|
243
|
+
}
|
|
183
244
|
export const trackerCommand = new Command('tracker')
|
|
184
|
-
.description('Show or set the machine\'s issue tracker provider')
|
|
245
|
+
.description('Show or set the machine\'s issue tracker provider and tracker servers')
|
|
185
246
|
.option('--status', 'Show the provider in effect and its learned conventions file, and re-arm background inference')
|
|
186
247
|
.option('--set <id>', 'Set the machine\'s default issue tracker provider: github, jira, or linear')
|
|
248
|
+
.option('--servers <names>', 'Set the tracker servers the Git and Tracker agents may call: comma-separated names, or "default"')
|
|
187
249
|
.action(async (options) => {
|
|
188
250
|
const devflowDir = getDevFlowDirectory();
|
|
189
|
-
const hasFlag = options.status || options.set !== undefined;
|
|
251
|
+
const hasFlag = options.status || options.set !== undefined || options.servers !== undefined;
|
|
190
252
|
if (!hasFlag) {
|
|
191
253
|
p.intro(color.bgCyan(color.white(' Tracker ')));
|
|
192
254
|
const validIds = TRACKER_PROVIDERS.map(t => `${t.id} — ${t.hint}`).join('\n ');
|
|
193
255
|
p.note(`${color.cyan('devflow tracker --status')} Show the provider and learned conventions\n` +
|
|
194
|
-
`${color.cyan('devflow tracker --set <id>')} Set the machine's default provider\n
|
|
256
|
+
`${color.cyan('devflow tracker --set <id>')} Set the machine's default provider\n` +
|
|
257
|
+
`${color.cyan('devflow tracker --servers <names>')} Set the tracker servers the agents may call\n\n` +
|
|
195
258
|
`Valid provider IDs:\n ${validIds}`, 'Usage');
|
|
196
259
|
p.outro(color.dim('github is the default — devflow tracker --set github returns to it'));
|
|
197
260
|
return;
|
|
@@ -206,7 +269,17 @@ export const trackerCommand = new Command('tracker')
|
|
|
206
269
|
}
|
|
207
270
|
setProvider = parsed.value;
|
|
208
271
|
}
|
|
209
|
-
|
|
272
|
+
// Validate --servers the same way: a bad name exits before any I/O, never repaired.
|
|
273
|
+
let setServers;
|
|
274
|
+
if (options.servers !== undefined) {
|
|
275
|
+
const parsed = parseTrackerServers(options.servers);
|
|
276
|
+
if (!parsed.ok) {
|
|
277
|
+
p.log.error(parsed.error);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
setServers = parsed.value;
|
|
281
|
+
}
|
|
282
|
+
const manifest = await readManifest(devflowDir, { onWarning: (message) => p.log.warn(message) });
|
|
210
283
|
if (!manifest) {
|
|
211
284
|
p.log.error('No manifest found. Run devflow init first.');
|
|
212
285
|
process.exit(1);
|
|
@@ -238,6 +311,7 @@ export const trackerCommand = new Command('tracker')
|
|
|
238
311
|
selection,
|
|
239
312
|
conventions,
|
|
240
313
|
mechanics,
|
|
314
|
+
servers: { list: effectiveTrackerServers(current), isDefault: current.servers === undefined },
|
|
241
315
|
inference,
|
|
242
316
|
}), 'Tracker Status');
|
|
243
317
|
if (!statusRearm.ok)
|
|
@@ -248,13 +322,34 @@ export const trackerCommand = new Command('tracker')
|
|
|
248
322
|
return;
|
|
249
323
|
}
|
|
250
324
|
// ── Set ────────────────────────────────────────────────────────────────────
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
325
|
+
// `--set` and `--servers` are independent settings and may be given together: the provider
|
|
326
|
+
// first, then the servers against the state it left.
|
|
327
|
+
const messages = [];
|
|
328
|
+
let state = current;
|
|
329
|
+
if (setProvider !== undefined) {
|
|
330
|
+
const outcome = await runTrackerSet({
|
|
331
|
+
devflowDir,
|
|
332
|
+
current,
|
|
333
|
+
requested: setProvider,
|
|
334
|
+
io: buildTrackerSetIO(),
|
|
335
|
+
});
|
|
336
|
+
messages.push(...outcome.messages);
|
|
337
|
+
state = { ...current, provider: outcome.provider };
|
|
338
|
+
}
|
|
339
|
+
let failed = false;
|
|
340
|
+
if (setServers !== undefined) {
|
|
341
|
+
const outcome = await runTrackerServers({
|
|
342
|
+
devflowDir,
|
|
343
|
+
claudeDir: getClaudeDirectory(),
|
|
344
|
+
current: state,
|
|
345
|
+
requested: setServers,
|
|
346
|
+
io: buildTrackerServersIO(),
|
|
347
|
+
});
|
|
348
|
+
messages.push(...outcome.messages);
|
|
349
|
+
state = outcome.state;
|
|
350
|
+
failed = !outcome.ok;
|
|
351
|
+
}
|
|
352
|
+
for (const msg of messages) {
|
|
258
353
|
switch (msg.level) {
|
|
259
354
|
case 'success':
|
|
260
355
|
p.log.success(msg.text);
|
|
@@ -270,7 +365,9 @@ export const trackerCommand = new Command('tracker')
|
|
|
270
365
|
break;
|
|
271
366
|
}
|
|
272
367
|
}
|
|
273
|
-
if (
|
|
368
|
+
if (failed)
|
|
369
|
+
process.exit(1);
|
|
370
|
+
if (setProvider !== undefined && state.provider !== DEFAULT_TRACKER_PROVIDER) {
|
|
274
371
|
p.log.info(color.dim('Your issue conventions are learned in the background at the next session start'));
|
|
275
372
|
}
|
|
276
373
|
});
|